I'm trying to do something pretty simple, that apparently is not that simple. I have a domain class:
class Regex {
String name
String Regex
}
and another class:
class RegexRef {
int sequenceNumber
Product product
Regex regex
}
Now, in a select, I want to list the RegexRef instances, with the name of the regex as the optionValue as such:
<g:select name="regexRef.id" from="${com.mycompany.RegexRef.list()}" optionKey="id" size="5" optionValue="regex.name" value="${actionRefInstance?.regexRef?.id}" />
but this doesn't work. It throws:
Exception Message: No such property: regex.name for class: com.jetheaddev.RegexRef
I can do this mis-direction in other constructs...
<g:link controller="regexRef" action="show" id="${actionRefInstance?.regexRef?.id}">${actionRefInstance?.regexRef?.regex.name.encodeAsHTML()}</g:link>
and it works fine.
With optionValue="regex.name" the <g:select/> is trying to retrieve the property of RegexRef as:
regexRefInstance."regex.name"
To retrieve the name property on the Regex class do:
<g:select optionValue="${{it.regex.name}}"/>
which will run the optionValue Closure on each entry in the list and retrieve the related name property.
Related
I have the following domain classes:
Airport:
class Airport {
String airportName
List<Flight> flights
static hasMany = [flights: Flight]
static mappedBy = [flights: "departureAirport"]
}
Flight:
class Flight {
String flightName
Integer numOfStaff
Airport departureAirport
}
I have in a form the following input fields which is correctly prints the saved airport and it's flights:
<input name="airportName" value="${airportInstance.airportName}" />
<input name="id" value="${airportInstance.id}" />
<input name="version" value="${airportInstance.version}" />
<g:set var="counter" value="${0}" />
<g:each in="${airportInstance?.flights?}" var="f">
<div>
name : <input name="flights[${counter}].flightName" value="${f.flightName}" />
id : <input name="flights[${counter}].id" value="${f.id}" />
numOfStaff : <input name="flights[${counter}].numOfStaff" value="${f.numOfStaff}" />
<g:set var="counter" value="${counter + 1}" />
</div>
</g:each>
I post the form to the following controller action:
#Transactional
def update() {
Airport airportInstance = Airport.get(params.id)
// doesn't work as well
// airportInstance.properties = params
bindData(airportInstance, params)
airportInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(
code: 'default.updated.message',
args: [
message(
code: 'Airport.label',
default: 'Airport'
),
airportInstance.id
]
)
redirect airportInstance
}
'*'{ respond airportInstance, [status: OK] }
}
}
When I modify airportName or some of the flights properties (e.g.: numOfStaff) and I click update (post it to the controller action), Grails does his work correctly and updates my changes.
But if I manually add a new instance in the view e.g.: if there were two flights flights[0] and flights[1] listed by g:each and I add flights[2] it doesn't create a new instance of Flight and doesn't save it.
The same if I try to delete an existing flight from the view and post it to the controllers update action.
I'm using grails version 2.3.9
My questions are:
Am I doing something wrong?
Is it a Grails bug or Grails developers implemented bindData method like this for some reason?
What is the best way in Grails to implement issue like above (I mean giving the name attributes like foo[index].prop where foo is a collection in the entity).
Sorry for the junk html.
Thank you guys, for the answers.
I found the solution. If I declare a controller action like:
def update(Airport airport) {
// ...
}
In this case before the controller action is executed Grails will automatically create an instance of the Airport class and populate its properties by binding the request parameters.
If there is an id request parameter then instead of invoking the domain class constructor to create a new instance a call will be made to the static get method on the domain class and the value of the id parameter will be passed as an argument. Whatever is returned from that call to get is what will be passed into the controller action. This means that if there is an id request parameter and no corresponding record is found in the database then the value of the command object will be null.
See Grails Reference Documentation: http://grails.org/doc/latest/guide/theWebLayer.html#commandObjects
I have a parent-child domain-class relation in Grails as shown, and I want to retrieve all the children of one specific parent object in order to display them in a HTML select component for the user to pick.
class Parent {
static hasMany = [children: child]
}
class child {
string name
Parent parent
static belongsTo = [Parent]
}
I know I should use a Grails' select tag of the form:
<g:select name="user.company.id"
from="${parent}"
value="${user?.company.id}"
optionKey="id" />
I tried doing this, but it does not seem to work:
<g:select name="child.id"
from="${the parent.children}"
optionKey="id" />
Any suggestions on what the correct syntax is? Thanks
Try this for a drop-down:
<g:select name="children"
from="${parent.children}"
optionKey="id" optionValue="name"/>
For multiple selects you can use:
<g:select name="children"
from="${parent.children}"
value="${parent.children.id}"
optionKey="id" optionValue="name"
multiple="true"/>
I'm using Grails 2.2.3 and Fields plugin 1.3.
I want to customize fields to manage one-to-many relationships using select tag.
In views/_fields/oneToMany/_input.gsp I have:
<g:select name="${property}.id" from="${type.list()}" optionKey="id" value="${value}" class="form-control one-to-many" noSelection="['null': "${label}"]" />
But type is a set, so I can't use list function.
How can I retrieve target domain class?
As long as you use a Map to declare the relationship, for example:
static hasMany = [ books: Book ]
You can get the list of the referenced domain, which is the key from the hasMany property of the bean, so the from attribute should change to
from="${bean.hasMany[property].list()}"
Alternatively you can pass the list to the _input.gsp template prefixing the variable name with input-, e.g.
<f:field property="books" input-domainList="${bookInstaceList}" />
In the _input.gsp template you can use the variable as follows:
from="${domainList}"
or mixing both methods:
from"${domainList ?: bean.hasMany[property].list()}"
I found this solution for the problem, this code work fine with one to many and one to one
<div class="input-group">
<g:select name="${persistentProperty.toString().contains('one-to-many')?property:property+'.id'}" value="${value?.id}" required="${required}"
noSelection="${['null':'Escriba...']}" class="select2 col-md-6"
multiple="${persistentProperty.toString().contains('one-to-many')?true:false}" from="${grailsApplication.getDomainClass(persistentProperty.properties['associatedEntity'].toString()).clazz.list()}"
optionKey="id"></g:select>
</div>
This solution can be helpful. I found in the documentacion of Fields Plugin, the persistentProperty is a class type DomainProperty but their methods doesn't works and I found the correct class is org.grails.datastore.mapping.model.MappingFactory
you can use .toArray() method on your Set instead of .list().
ale
You can use the code below to identify the Element of your collection
def className = bean.hasMany[property].properties.typeName
See my "views/_fields/oneToMany/_input.gsp" implementation:
<%# page defaultCodec="html" %>
<%
def className = bean.hasMany[property].properties.typeName
def simpleName = Class.forName(className).simpleName
def beanSimpleName = bean.class.simpleName
def createUri = "/${simpleName.toLowerCase()}/create?${beanSimpleName.toLowerCase()}.id=${bean.id}"
%>
<a id="add${simpleName}" name="add${simpleName}" href="${createLink(uri: "${createUri}")}">
Add ${simpleName}
</a>
<ul>
<g:each var="item" in="${value}">
<li>
${item}
</li>
</g:each>
</ul>
I've scoured the web for almost a day and can't seem to find a solution to my problem.
I have a lookup table called Hobby which has a bunch of hobbies in it (camping, hiking, biking, etc). The hobbies table is populated during the bootstrap. Grails creates a hobby table with an id, and description field.
I have a domain object called Applicant. An applicant can have zero or more hobbies. I've declared the domain like this:
class Applicant {
static hasMany = [hobbies:Hobby]
List <Hobby> hobbies = LazyList.decorate(new ArrayList(), FactoryUtils.instantiateFactory(Hobby.class));
}
In my controller I'm using a command object for the page that will allow the applicant to select their hobbies. It is defined as:
class LifestyleCommand {
List <Hobby> hobbies = LazyList.decorate(new ArrayList(), FactoryUtils.instantiateFactory(Hobby.class));
}
My gsp looks like this:
<g:each var="item" in="${Hobby.list()}" status="i">
<g:set var="newline" value="${(i % 3) == 0 ? 'newline' : ''}" />
<div class="formcheckbox columns3 ${newline}">
<g:checkBox name="hobbies_${item.id}" optionKey="id" value="${item.id}" />
<label>${item.description}</label>
</div>
</g:each>
The page will display all of the hobbies in the page correctly. However when I try to submit the form back to the controller the list in the LifeStyleCommand object is null. I'm not sure if my gsp has the g:checkBox variables set correctly, and I'm not sure if I declared the List in the command object correctly either. Once I get the data to be sent back to the controller, my next problem to overcome will be copying the data from the command object to the Applicant. Any help would be appreciated.
I've tried using just a plain List in the command but grails complains about type conversions when the form is submitted.
EDIT:
Here is what I got to work:
class Applicant {
static hasMany = [hobbies:Hobby]
//I removed the List <hobby>... code
...
}
My command object:
class LifestyleCommand {
Set <Hobby> hobbies;
}
gsp:
<g:each in="${Hobby.list()}" var="hobby">
<g:set var="checked" value="${ command?.hobbies.find{h->h.id == hobby.id } != null }" />
<g:checkBox value="${checked}" name="${ 'hobby' +'[' + hobby.id + ']'}"/>${hobby.description}</td>
</g:each>
And my new controller code:
Hobby.list().each{hobby->
if (params["hobby[${hobby.id}]"] == 'on') {
applicant.addToHobbies(hobby)
}
else if (applicant.getHobbies().contains(hobby)) {
applicant.removeFromHobbies(hobby)
}
}
Everything is working. There may be better ways, and I haven't done any refactoring yet but having it work is a starting point.
The problem is with your GSP. When you need a list of objects, you have to maintain a standard name for your checkbox:
<g:checkBox name="hobbies[$i].id" ... />
This will be mapped correctly to your command list.
How do I get Grails data binding to correctly bind referenced objects (such as Country in the example below)?
Given the following two Grails domain classes ..
class User {
String username
Country country
}
class Country {
String name
}
.. and the following HTML form ..
<g:form>
<g:textField name="user.username" value="${user.username}" />
<g:select name="user.country" from="${Country.list()}" optionKey="id" />
</g:form>
.. and the following code in the corresponding action ..
User user = new User(params["user"])
.. I would have hoped that user.username and user.country would get bind. However, it appears as if username.username gets bind, whereas user.country is not. What is the correct syntax to bind referenced objects (user.country in this example)?
The binding of the "country" property starts working if ..
<g:select name="user.country" from="${Country.list()}" optionKey="id" />
.. is changed to ..
<g:select name="user.country.id" from="${Country.list()}" optionKey="id" />
You should also look into command objects. They can validate and bind all of your parameters at once.