I am using command object and I have used a domain object inside my command object as given below.
class JobCommand {
List<Country> countries
String name
String age
.....
}
Country Domain is as follows:
#EqualsAndHashCode(includes="id")
class Country{
String id
String name
Date createDate
......
}
I am accessing this jobCommand object from my JobController to and also in my gsp page to set the value in my Country field.
I have a search field named Country in my gsp page and it has options of Countries like, USA, India, Iran etc.
<g:select name="countries" id="country"
from="${Country.findAll()}"
value="${jobCommand ?.countries}"
multiple="true"
optionKey="id"
optionValue="name"
/>
When I select multiple Countries, and click search button, I get the exception "Could not find matching constructor for: package.Country(java.lang.String)".
There are 2 ways to Bind Params to Command object.
1. Use bindData (http://docs.grails.org/3.1.1/ref/Controllers/bindData.html)
2. To write our own Converters. I had the same issue and I wrote my own converters.
Steps to write own converter :
create converter class with below methods:
boolean canConvert(value) {
value instanceof String
}
def convert(value) {
//your logic
}
Class<?> getTargetType() {
//your type
}
Register your converter in resources.groovy inside bean{}
Related
In my domain model, some classes have a collection of enums as a field. I have modeled it in two differente ways, as an embedded collection:
class A {
String name
Set<Enumeration> enumerations
static embedded = ['enumerations']
}
enum Enumeration {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3
}
And also as a hasMany collection:
class A {
String name
static hasMany = [enumerations:Enumeration]
}
enum Enumeration {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3
}
In both cases, I can add enum values to the collection in BootStrap.groovy in the following way:
A.withTransaction { status ->
def a1 = new A( name:"a1" )
a1.addToEnumerations( Enumeration.ENUM_VALUE_1 )
a1.addToEnumerations( Enumeration.ENUM_VALUE_2 )
}
Using scaffolding, I can see the content of the enumeration collection in the index and show pages, but in the edit and create pages, only the label is shown, no widget is displayed.
Which is the simplest way to show a widget, e.g. a multiple select, for this kind of fields in Grails 4 (I am using Grails 4.0.3)?
Thanks in advance.
Which is the simplest way to show a widget, e.g. a multiple select,
for this kind of fields in Grails 4 (I am using Grails 4.0.3)?
You can use the select tag.
<g:select multiple="true" name="someName" from="${somePackage.SomeEnumClassName}"/>
This is the process I followed after #jeff-scott-brown answer:
I generated the views for class A:
generate-views A
Then in grails-app/views/a/edit.gsp and grails-app/views/a/create.gsp I changed this element:
<f:all bean="a"/>
into these other elements:
<f:with bean="a">
<f:field property="name"/>
<f:field property="enumerations">
<g:select
multiple="true"
name="${property}"
from="${Enumeration}"
value="${a?.enumerations}"
/>
</f:field>
</f:with>
And it works for both the embedded and the hasMany approaches.
Should not this be the default scaffolding for collections of enumerations in Grails?
Well if this is for validating data for your database, you can use the 'inList' constraint on your domain as the same constraint by extracting that as a 'List' for you to use:
def class = grailsApplication.getDomainClass(className)
def props = class.getConstrainedProperties()
props.each(){ k,v ->
if(k=='theColumnName-you-are-trying-to-get'){
return v.getInList()
}
}
This is a simpler solution without having to generate the views, i.e. no need to modify edit.gsp and create.gsp. Just using the fields plugin as suggested by #jeff-scott-brown.
Assuming this is your domain class:
package mypackage
class MyClass {
// ...
// properties
// ...
Set<MyEnumeration> enumerations
// no need to declare it as embedded
// static embedded = ['enumerations']
}
enum MyEnumeration {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3
}
And that you have generated a scaffold controller for it, i.e.
package mypackage
class MyClassController {
static scaffold = MyClass
}
The only thing you have to do is creating the file grails-app/views/_fields/myClass/enumerations/_widget.gsp with the following content:
<g:select
multiple="true"
name="${property}"
from="${mypackage.MyEnumeration}"
value="${myClass?.enumerations}"
/>
It also works if you declare the collection of enumerations as a hasMany association, i.e.
package mypackage
class MyClass {
// ...
// properties
// ...
static hasMany = [enumerations:MyEnumeration]
}
enum MyEnumeration {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3
}
I can't override grails getter method and becoming crazy.
What I want is use a double value and a string value to get a formatted data, but when I'm typing my override method into String, the double value is null and when into double, it obviously get me an error because a String is returned !
I get a domain class like that :
class Project {
...
String currency
Double initialTargetAmount
...
}
First Override method (initialTargetAmount is null in this case) :
//#Override Comment or uncomment it does not make any change to the behaviour
public String getInitialTargetAmount() {
println "${initialTargetAmount} ${currency}" // display "null EUR"
"${initialTargetAmount} ${currency}" // initialTargetAmount is null
}
Second method :
//#Override Comment or uncomment it does not make any change to the behaviour
public Double getInitialTargetAmount() {
println "${initialTargetAmount} ${currency}" // display "1000.00 EUR"
"${initialTargetAmount} ${currency}" // Error : String given, Double expected
}
Any help is welcome.
Snite
Groovy has dynamic getters and setters.
So, initalTargetAmount field "creates" automatically a Double getInitialTargetAmount method. Which is why it works when you have the Double return Type. But when you set String, getInitialTargetAmount automatically refers to a field String initalTargetAmount which doesn't exist
Try changing the name of the method, for example getInitialAmountWithCurrency() and it will work. Maybe your best bet will be to override toString() method ^^
Your getter should be always the same type of your field, and it's noot a good approach to change the getter like this, because Grails (Hibernate internally) will understand that your object instance changed and will try to update it ( it will check the old and new values).
You're trying in fact is to have a String representation of your amount, so you have a couple of options to this:
1 - A new method
Creating a new method that returns String will not interfere in the hibernate flow and you can use it anywere.
class Project {
...
String currency
Double initialTargetAmount
...
String displayTargetAmount() {
"${initialTargetAmount} ${currency}"
}
}
2 - TagLib
Depending on your needs, you could create a TagLib to make this custom representations of your class. This can include html formatting.
class ProjectTagLib {
static namespace = "proj"
def displayAmount = { attrs ->
if(!attrs.project) {
throwTagErrro("Attribute project must be defined.")
}
Project project = attrs.remove('project')
//just an example of html
out << "<p>${project.initialTargetAmount} , ${project.currency}</p>"
}
}
I have textfield for birthDate. When a user enter invalid date, let say for example a String, error message successfully displayed as fielderror. But in my console, I got this error:
java.lang.NoSuchMethodException: Profile.setBirthDate([Ljava.lang.String;)
Have I missed something that's why I encountered the error?
In your Action class you dont have a method called setBirthDate(String birthDate), add it your issue should be resolved.
Note check to see that you have placed all getter and setter in your action class for all properties.
I think in your JSP you have :
<s:textfield name="birthDate" />
Struts will try to map this to setBirthDate(String string), since this method is missing in your action hence the NoSuchMethodException
Update:
To convert String to Date:
public class MyStringToDateConverter extends StrutsTypeConverter {
public Object convertFromString(Map context, String[] values, Class toClass) {
//Parse String to get a date object
}
public String convertToString(Map context, Object o) {
// Get the string from object o
}
}
If you are using Annotation in your action class then add #Conversion() to your action
#Conversion()
public class MyAction extends ActionSupport{
public Date myDate = null;
#TypeConversion(converter="MyStringToDateConverter") //Fully qualified name so if this class is in mypackage then converter will be "myPackage.MyStringToDateConverter"
public void setMyDate(Date date) {
this.myDate = date;
}
}
If you dont want to use Annotation then you can look at the official documentation for example.
I have a one-to-one relationship, and have a combobox for selecting the other one. How do I change the display name of the combo box?
Here's a screenshot of the combo box, to make myself clear:
I'm new to grails, and are attempting to create a simple data-driven app for in house use.
Edit: My toString():
def toString = { "${naam} [${gemeente}]" }
Override the toString method of said domain class.
String toString() {
return this.name;
}
The sig needs to be String toString() { ... } (you are returning a def and it is a closure, not a method)
In grails, the select tag also has an optional attribute named "optionValue" that will allow you to define a bean field/property to display.
In Grails 3, the following works:
String toString() {
return "${name}"
}
in your example, this would be:
String toString() {
return "${naam} [${gemeente}]"
}
cheers!
I have a domain class containing a couple of fields. I can access them from my .gsps. I want to add a method to the domain class, which I can call from the .gsps (this method is a kind of virtual field; it's data is not coming directly from the database).
How do I add the method and how can I then call it from the .gsps?
To add a method, just write it out like you would any other regular method. It will be available on the object when you display it in your GSP.
def someMethod() {
return "Hello."
}
Then in your GSP.
${myObject.someMethod()}
If you want your method to appear to be more like a property, then make your method a getter method. A method called getFullName(), can be accessed like a property as ${person.fullName}. Note the lack of parentheses.
Consider class like below
class Job {
String jobTitle
String jobType
String jobLocation
String state
static constraints = {
jobTitle nullable : false,size: 0..200
jobType nullable : false,size: 0..200
jobLocation nullable : false,size: 0..200
state nullable : false
}
def jsonMap () {
[
'jobTitle':"some job title",
'jobType':"some jobType",
'jobLocation':"some location",
'state':"some state"
]
}
}
You can use that jsonMap wherever you want. In gsp too like ${jobObject.jsonMap()}