iberck
11/30/2016 - 7:40 PM

Grails databinding list (dinámico)

Grails databinding list (dinámico)

El siguiente ejemplo muestra cómo hacer databinding de propiedades dentro de un List de objetos, en la página se podrán escribir N objetos. Este ejemplo cubre la clásica página donde se van agregando items dinámicamente.

Este código espera que los campos vengan ordenados en el orden en que fueron escritos ya que html garantiza que los parámetros se escriban en la URL de acuerdo a cómo están escritos dentro de la página, la implementación de servlet no indica en qué orden se deben proporcionar los parámetros de la url, sin embargo la mayoría de los contenedores respetan el orden. En caso que un contenedor no respete el orden de los parámetros, se tendrá que escribir un índice para cada grupo de parámetros para luego ordenarlos en base a dicho índice.

El ejemplo promueve la alta cohesión ya que la descomposición del request para crear el command object se encuentra en el mismo command object.

Command object DistReserva:

@Validateable
class DistReserva {

    @BindUsing({ _this, source ->
        def list = new ArrayList<DistHabitacion>()
        String[] id = source["id"]
        String[] numAdultos = source["numAdultos"]
        String[] numNinos = source["numNinos"]

        for (int i = 0; i < id.length; i++) {
            def distHabitacion = new DistHabitacion()
            if (id[i].isLong()) {
                distHabitacion.id = id[i] as Long
            }
            if (numAdultos[i].isInteger()) {
                distHabitacion.numAdultos = numAdultos[i] as Integer
            }
            if (numNinos[i].isInteger()) {
                distHabitacion.numNinos = numNinos[i] as Integer
            }
            list.add(distHabitacion)
        }
        return list
    })
    List<DistHabitacion> distribucion

    static constraints = {
        distribucion(nullable: false, validator: { distribucion ->
            distribucion.every { it.validate() }
        })
    }

}

Command Object DistHabitacion:

@Validateable
class DistHabitacion {
    Long id
    Integer numAdultos
    Integer numNinos
}

Controlador:

class TestController {

    def index() {
        render view: "index"
    }

    def formSubmit(DistReserva distReserva) {
        if (distReserva.hasErrors()) {
            render view: "index", model: [cmd: distReserva]
            return
        }

        render view: "index", model: [cmd: distReserva]
    }
}

GSP:

<body>

<g:form>
    <g:each in="${cmd?.distribucion}" var="it" status="i">
        <g:hasErrors bean="${it}">
            <div class="alert alert-danger">
                Error en panel: ${i + 1}
                <g:renderErrors bean="${it}" as="list"/>
            </div>
        </g:hasErrors>
    </g:each>

    <g:each in="${0..2}" var="i">
        <g:hiddenField name="id" value="${i+1}"/>
        Panel ${i+1}
        <hr/>
        Num adultos: <input type="number" name="numAdultos" value="${fieldValue(bean: cmd?.distribucion?.get(i), field: "numAdultos")}"><br/>
        Num niños: <input type="number" name="numNinos" value="${fieldValue(bean: cmd?.distribucion?.get(i), field: "numNinos")}"><br/>
    </g:each>

    %{--Necesario para que haga el databinding de la propiedad distribucion y se ejecute el @BindUsing--}%
    <g:hiddenField name="distribucion" value="0"/>
    <g:actionSubmit value="enviar" action="formSubmit"></g:actionSubmit>
</g:form>

</body>