Last updated by luisfpg
3 years ago
Extended Data Binding Plugin
The extended data binding plugin allows configuring the DataBinder which controllers will use to parse the user input and populate objects, as well as wrapping objects to format data as strings in order to display data.Contents
- #Features
- #Installation
- #Application-wide DataBinder and BeanWrapper configuration
- #Controllers dynamic methods
- #Custom tags
- #Example application
Features
- Allow customization of the DataBinder that will be used to parse user-defined input and populate objects (typically domain objects) with custom PropertyEditors on both application-wide and controller-specific levels.
- The same property editors are also used on BeanWrappers which will be used by a custom utility class, org.
- Provide an utility class, br.inf.freeit.extendeddatabinding.WrappedBean, which uses a BeanWrapper to access properties and to retrieve PropertyEditors to format data as String.
- Extend controllers with dynamic methods to allow data binding and bean wrapping.
- Provide a custom tag to wrap beans on views.
Installation
- Download the latest version of the plugin
- cd to your project root
- run grails install-plugin <path-to-file grails-extended-data-binding-X.X.zip
Application-wide DataBinder and BeanWrapper configuration
In order to configure application-wide PropertyEditors, you should set closures under the ServletContext (application) scope, under the following attributes:- _newDataBinder_: Takes the request and the object as parameters and should return a GrailsDataBinder instance
- _newBeanWrapper_: Also takes the request and the object and should return a BeanWrapper instance
Controller-specific DataBinder and BeanWrapper configuration
To configure the DataBinder and BeanWrapper on the controller, you can define the following methods:- _registerCustomEditors_: Invoked to configure both DataBinder and BeanWrapper. Should be used to register PropertyEditors that are specific for that controller
- _initBinder_: Only invoked when configuring a DataBinder, should be used to initialize other properties on the DataBinder, such as _setDisallowedFields_.
Controllers dynamic methods
This plugin adds some methods to controllers:- _getBinder_: Takes an object and returns a DataBinder for that object, setting it on the request under the attribute dataBinder
- _wrapBean_: Takes an object and returns a WrappedBean instance. A WrappedBean is an utility class that uses a BeanWrapper and converts properties to String using registered PropertyEditors
- _bind_: Takes an object, performs the data binding (using the getBinder() method) and returns the object itself
Custom tags
The following tags are added to the default g namespace:- wrap: creates a WrappedBean and exports it to a variable on a given scope. It accepts the following attributes:
- _bean_: The object instance (required)
- _var_: An attribute on a given scope to export the WrappedBean instance (required)
- _scope_: The scope to export the WrappedBean instance (optional, defaults to page)
- eachWrapped: works like _g:each_, but wraps each result before exporting it to a variable. Attributes:
- _in_: The collection to iterate (required)
- _var_: The name of the variable that will hold the wrapped instance (required)
- status: The name of the variable that will hold the current loop index (optional)
Example
Here is an example application, that uses a sample domain object called Person, as follows:class Person {
String name
Calendar birthDate
BigDecimal income static constraints = {
name maxSize: 100
birthDate nullable: true
income nullable: true
}
}import java.text.DateFormat import java.text.DecimalFormat import java.text.DecimalFormatSymbols import org.springframework.beans.BeanWrapperImpl import org.springframework.beans.propertyeditors.CustomNumberEditor import org.codehaus.groovy.grails.web.binding.GrailsDataBinderclass GlobalPropertyEditorConfig { static newDataBinder = { request, object -> def binder = GrailsDataBinder.createBinder(object, GrailsDataBinder.DEFAULT_OBJECT_NAME, request) registerCustomEditors(request, binder) return binder } static newBeanWrapper = { request, object -> def beanWrapper = new BeanWrapperImpl(object) registerCustomEditors(request, beanWrapper) return beanWrapper } private static void registerCustomEditors(request, binder) { def numberFormat = new DecimalFormat("#,##0.00", new DecimalFormatSymbols(request.locale)) binder.registerCustomEditor(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, numberFormat, true)) def dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, request.locale) dateFormat.lenient = false binder.registerCustomEditor(Calendar.class, new CalendarEditor(dateFormat, true)) } }
import java.text.DateFormat import org.springframework.beans.propertyeditors.CustomDateEditorclass CalendarEditor extends CustomDateEditor { public CalendarEditor(DateFormat dateFormat, boolean allowEmpty) { super(dateFormat, allowEmpty) } public void setAsText(String text) { super.setAsText(text) def value = this.value if (value instanceof Date) { Calendar cal = Calendar.instance cal.time = value this.value = cal } } public String getAsText() { def value = this.value if (value instanceof Calendar) { this.value = value.time } return super.getAsText() } }
class BootStrap { def init = { servletContext ->
servletContext.setAttribute("newDataBinder", GlobalPropertyEditorConfig.&newDataBinder)
servletContext.setAttribute("newBeanWrapper", GlobalPropertyEditorConfig.&newBeanWrapper) new Person(name:"John", birthDate:new GregorianCalendar(1970, 0, 18), income:5609.87).save()
} def destroy = {
}
}class PersonController { def index = { redirect(action:list,params:params) } // the delete, save and update actions only accept POST requests
def allowedMethods = [delete:'POST', save:'POST', update:'POST'] def list = {
if(!params.max) params.max = 10
[ personList: Person.list( params ) ]
} def show = {
def person = Person.get( params.id ) if(!person) {
flash.message = "Person not found with id ${params.id}"
redirect(action:list)
}
else { return [ person : wrapBean(person) ] }
} def delete = {
def person = Person.get( params.id )
if(person) {
person.delete()
flash.message = "Person ${params.id} deleted"
redirect(action:list)
}
else {
flash.message = "Person not found with id ${params.id}"
redirect(action:list)
}
} def edit = {
def person = Person.get( params.id ) if(!person) {
flash.message = "Person not found with id ${params.id}"
redirect(action:list)
}
else {
return [ person : wrapBean(person) ]
}
} def update = {
def person = Person.get( params.id )
if(person) {
bind(person)
if(!person.hasErrors() && person.save()) {
flash.message = "Person ${params.id} updated"
redirect(action:show,id:person.id)
}
else {
render(view:'edit',model:[person:wrapBean(person)])
}
}
else {
flash.message = "Person not found with id ${params.id}"
redirect(action:edit,id:params.id)
}
} def create = {
def person = bind(new Person())
return ['person':wrapBean(person)]
} def save = {
def person = bind(new Person())
if(!person.hasErrors() && person.save()) {
flash.message = "Person ${person.id} created"
redirect(action:show,id:person.id)
}
else {
render(view:'create',model:[person:wrapBean(person)])
}
}
}