As in Struts n JSF, we link a form to a bean(eg. backing bean in JSF).
But in grails, we are using params to set values of the bean. Is there any other way to automatically map a form to the bean?
Grails has the possibility to auto bind values from the params map to a given domain instance / command object. This can be achieved by defining the correct beans as a parameter for your controller methods. Take this example:
Controller:
class AuthorController {
def save(Author author) {
// matching param values are bound to the author instance
assert params.name == "myName"
assert author.name == "myName"
}
}
gsp with form:
<g:form controller="author" action="save">
<g:field name="name" value="myName" />
<g:submitButton name="save" value="Save" />
</g:form>
Sometimes is better to use the Command Objects provided by Grails, the domain class can be wrapped only matching the name of the form fields with the attributes names of the class.
But Command Objects can provide an intermediate layer of validation and abstraction to generate the model bean.
http://grails.org/doc/2.3.0/guide/single.html#commandObjects
Related
I'm wondering, in the scaffold controller and views, how the fields you fill in the "create" page get updated to your domain class instance before the save action. I'm on Grails 2.4.4.
Take an example, I have one class called Customer and I generate the controller and views all in the default way.
class Customer {
String name;
String email;
String address;
String mobile;
}
And when you run the application and in the generated green-styled index page, click on "create new customer", one Customer instance will be created as the link goes to "create" action.
<ul>
<li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
<li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
</ul>
In controller:
def create() {
log.info "A customer object is created here.";
Customer c=new Customer(params)
respond c
}
But now you haven't filled in the form on all the fields yet! And after you fill in the form in create.gsp, the link will point you directly to "save" action.
<g:form url="[resource:customerInstance, action:'save']" >
<fieldset class="form">
<g:render template="form"/>
</fieldset>
<fieldset class="buttons">
<g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" />
</fieldset>
</g:form>
But in the save action I see nothing related to setting the fields on this instance as the form goes. Where is it done?
#Transactional
def save(Customer customerInstance) {
if (customerInstance == null) {
notFound()
return
}
if (customerInstance.hasErrors()) {
respond customerInstance.errors, view:'create'
return
}
customerInstance.save flush:true
//omit everything after save here
}
Grails does this for you automatically with Data Binding. Grails controllers can take two categories of arguments: basic objects, and complex objects. The default behavior is to map HTTP request parameters to action arguments by name, wherever applicable. For example, say we have a controller like so:
def doSomething(Integer magicNumber) {
println "The magic number is $magicNumber"
}
and a view that contains a field like this:
<g:textField name="magicNumber" value="${magicNumber}" />
When the form is submitted to the doSomething action, Grails will automatically take the magicNumber request param, convert it from a String to an Integer and pass it to the action.
Complex types (like domain objects) are treated as command objects. Command objects function in a similar manner to basic objects in regards to data binding. Notice how in your code, the save action takes a Customer instance as an argument? Behind the scenes, Grails takes the HTTP request parameters and binds those parameters to the properties of a given object, in this case, a Customer. From the documentation:
Before the controller action is executed Grails will automatically create an instance of the command object class and populate its properties by binding the request parameters.
In other words, Grails will look at all of the incoming request parameters and attempt to bind those to properties on the object that you've stated as an argument to the action. In addition, it will perform validation for you. It's essentially the same as:
#Transactional
def save() {
Customer customerInstance = new Customer(params)
customerInstance.validate()
if (customerInstance == null) {
notFound()
return
}
if (customerInstance.hasErrors()) {
respond customerInstance.errors, view:'create'
return
}
customerInstance.save flush:true
//omit everything after save here
}
I am having a problem saving a domain instance in grails; The domainInstance that's passed to the default update method in my controller is NULL. The GSP page I am submitting from is not the default edit page. I have certain values from DB that need to be saved. I have the form tag defined on the page that contains the values I need to submit, as follows.
<g:form id="sampleForm" url="[resource:domainInstance, controller:'cntrlrName', action:'update']" method="PUT" >
I also have a version field which looks like this.
<g:hiddenField name="version" value="${domainInstance?.version}" />
My g:submit is as follows
<g:actionSubmit action="update" value="${message(code: 'default.button.update.label', default: 'Update')}" />
Why is my domain instance null? What am I missing?
This is the common mistake one could make. The attribute id in <g:form> tag is not the id attribute of HTML tags but it is the id to use in the link for default mapping of Grails i.e.
"/$controller/$action/$id" {}
So change your tag as:
<g:form name="sampleForm" id="${domainInstance.id}" controller="cntrlrName" action="update" method="PUT">
You can pass the domainInstance but I feel it is better practice to pass the id instead of the object. Try passing the id of the domain instance and then reading the object in the controller.
<g:form name="sampleForm" action="action" controller="controller" id="${domainInstance.id}" ></g:form>
// in controller
def resource = Domain.read(params.id)
another approach could be to pass the domainInstance as a hiddenField
<g:hiddenField name="resource" value="$domainInstance" />
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 complex object that if I render in this way:
<%=Html.EditorFor(x => x.ContactRoleDto.Competencies[0].Name) %>
The above generates the following element which has the name and id tags that I want:
<input Size="40" id="ContactRoleDto_Name" maxlength="100" name="ContactRoleDto.Name" type="text" value="" />
I would like to render a tag with the correct id and name attributes that are in the same form as above, i.e. ContactRoleDto.Competencies[0].Name".
How is the best way to achieve this?
All I really want is the ability to pull out the correct id and name fields that will help me model bind a table that is dynamically generated or rendered.
Is ModelMetaData the best way to go, I do not want to go the UIHint route.
You could use the ExpressionHelper.GetExpressionText method. For example:
Expression<Func<string, YourModel>> expression = x => x.ContactRoleDto.Competencies[0].Name;
string id = ExpressionHelper.GetExpressionText(expression);
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.