Grails custom databinding
Se utiliza para hacer un binding personalizado de la propiedad marcada con la anotación.
El closure @BindUsing
solo es lanzado para una propiedad, por lo tanto debe haber un parámetro en el request con el mismo nombre de la propiedad.
class Product {
// Product _this, DataBindingSource source
@BindUsing({ _this, source ->
// El parámetro source contiene los valores.
final String productId = source['productId']
// ID format is like {code}-{identifier},
// eg. TOYS-067e6162.
final productIdParts = productId.split('-')
// EL CLOSURE DEBE RETORNAR EL OBJETO QUE SE ASIGNARÁ A LA PROPIEDAD.
new ProductId(
code: productIdParts[0],
identifier: productIdParts[1])
})
ProductId productId
}
// Class for product identifier.
class ProductId {
String code
String identifier
}
Sirve para hacer un binding propiedad por propiedad hasta hacer el binding de todas las propiedades de la clase, para ello es necesario implementar la interfaz BindingHelper
.
@BindUsing se utiliza comunmente a nivel de propiedad, rara vez se utiliza a nivel de clase ya que utiliza un tipo genérico para el tipo de retorno lo cual significa que todas las propiedades de la clase deberían ser del mismo tipo. Además tiene un bug cuando en el request hay múltiples campos con el mismo nombre.
Nota: se hizo una prueba y @BindUsing a nivel de clase es ignorado en grails 2.5.2: http://stackoverflow.com/questions/22258653/grails-bindusing-on-a-class-being-ignored-and-bindusing-vs-valueconverter
@BindingFormat
permite hacer un binding personalizado a partir de un formato que viene de la anotación. Se define un FormattedValueConverter
el cual proporciona el valor del request y el formato que proviene de la anotación para poder crear un objeto de cualquier tipo el cual es el asignado a la propiedad marcada con esta anotación. El formato soporta i18n.
Un buen caso de uso es construir un Date
a partir de una fecha en cadena que viene del request y el formato indicado en la anotación de la propiedad.
Ejemplo que transforma el valor del request al formato indicado en la anotación:
package com.myapp.converters
import org.grails.databinding.converters.FormattedValueConverter
class FormattedStringValueConverter implements FormattedValueConverter {
def convert(value, String format) {
if('UPPERCASE' == format) {
value = value.toUpperCase()
} else if('LOWERCASE' == format) {
value = value.toLowerCase()
}
value
}
Class getTargetType() {
// specifies the type to which this converter may be applied
String
}
}
// grails-app/conf/spring/resources.groovy
beans = {
formattedStringConverter com.myapp.converters.FormattedStringValueConverter
}
import org.grails.databinding.BindingFormat
class Person {
@BindingFormat('UPPERCASE')
String someUpperCaseString
@BindingFormat('LOWERCASE')
String someLowerCaseString
}
Sirven para indicar a grails cómo debe hacer el databinding hacia cierto tipo de dato. Por ejemplo para que grails pueda hacer automáticamente databinding de objetos BigDecimal
a lo largo de toda la aplicación, es necesario definir un custom data converter para BigDecimal
.
Por ejemplo podría escribir un ValueConverter
de la clase Address
para que grails sepa cómo hacer el databinding de la cadena del request hacia la propiedad de tipo Address
.
Grails tiene definidos ValueConverters
para los tipos de dato básicos (Long
, Integer
, ...), esto le permite saber cómo transformar el valor en cadena del request a cada uno de los tipos de dato básicos.
Con la siguiente clase se indica a Grails cómo debe hacer el databinding cada que encuentre una propiedad de tipo BigDecimal
:
class BigDecimalValueConverter implements ValueConverter {
@Override
boolean canConvert(Object value) {
// La expresión regular acepta solo números decimales en el formato "23.22"
value instanceof String && value ==~ /([\+\-])*[0-9]*(\.[0-9]+)*/
}
@Override
Object convert(Object value) {
// log.trace("Convirtiendo el valor: $value a BigDecimal")
// Independientemente del locale el separador decimal que espera el constructor es siempre "."
return new BigDecimal(value)
}
@Override
Class<?> getTargetType() {
return BigDecimal
}
}
beans = {
defaultGrailsBigDecimalConverter(BigDecimalValueConverter)
}
class Account {
BigDecimal value
}
A diferencia de los ValueConverter
que se crea un objeto a partir de 1 valor del request, con estos editores se pueden crear objetos a partir de varios parámetros del request.
Un ejemplo común puede ser crear un objeto Date
a partir de los parámetros del request birthday_month
, birthday_date
y birthday_year
.