Arena - Bindings y demás controllers

Arena viene por defecto con mecanismos de binding que permiten mapear propiedades de vista con propiedades del modelo. No obstante, para ciertos casos especiales es útil utilizar o extender estos mecanismos de binding

Los componentes que vemos aquí están en el package org.uqbar.arena.bindings (del jar arena-core), salvo cuando se especifique otro.

Cómo funciona el Binding en Arena

El objeto Binding es una abstracción que vincula dos propiedades observables:

  • una del modelo
  • y otra de la vista.

El binding se completa con un Transformer que permite ajustar las diferencias entre los valores manejados por modelo y vista.

Binders

ObservableProperty<T>

Implementación default del binding, esta clase escucha los eventos que dispara el cambio de una propiedad en el modelo. Ejemplo de una asignación manual del Observable Property:

En este caso se permite modificar el modelo observado para que no sea el default de la vista (el panel principal). De lo contrario se puede utilizar el constructor con un solo parámetro (la propiedad observada del modelo de la vista, que es el que se toma por defecto):

O directamente usamos un factory method:

NotNullObservable

En esta implementación se escucha la propiedad de un modelo y se asocia a un valor booleano que la vista necesita (por ejemplo: enabled o visible), en base a que la propiedad tenga un valor o esté nula (true/false, respectivamente).

¿Qué necesito para generar nuevos observables?

En general estas dos implementaciones son suficientes para trabajar con Arena. No obstante, si lo necesitás podés crear tu propio ObservableProperty que escuche cambios en el modelo y los dispare a algún tipo de propiedad en la vista, definiendo estos métodos:

  • el constructor
  • configure(BindingBuilder binder): define un binding contra una propiedad, el binder se puede adaptar para que trabaje con un determinado transformer. Ejemplo de NotNullObservable:

ValueTransformer<M, V>

Interfaz que define cómo convertir y validar valores de la vista al modelo y viceversa (recordemos que el binding entre modelo y vista es bidireccional). Define esta interfaz:

  • M viewToModel(V valueFromView): convierte el valor de la vista al formato que el modelo necesita
  • V modelToView(M valueFromModel): convierte el valor del modelo al formato que la vista necesita
  • Class<M> getModelType(): devuelve la clase asociada al modelo
  • Class<V> getViewType(): devuelve la clase asociada a la vista

DateTransformer

Implementa ValueTransformer<Date, String>. Convierte a Date los Strings en formato dd/MM/yyy. Acepta valores nulos. Ejemplo de uso: creamos un textbox que ingresa una propiedad fechaDesde definida como Date en el modelo:

NotEmptyTransformer

  • Se ubica en el package org.uqbar.lacar.ui.model.transformer
  • Implementa ValueTransformer<String, Boolean>
  • Mapea un objeto String a un booleano que indica si el string es nulo o vacío

NotNullTransformer

  • Se ubica en el package org.uqbar.lacar.ui.model.transformer
  • Implementa ValueTransformer<Object, Boolean>
  • Mapea cualquier Object con un valor booleano que indica si es distinto de null

Quiero definir un nuevo Transformer, ¿qué necesito?

  • Definir las clases M y V para el modelo y la vista, respectivamente
  • implementar ValueTransformer de esas clases concretas M y V
  • definir los métodos getModelType() y getViewType() en base a las clases concretas
  • definir los métodos viewToModel() y modelToView() para indicar cómo se debe adaptar el valor de la vista al modelo y viceversa

Ejemplo: BigDecimalTransformer

Esta clase forma parte del ejemplo de las apuestas:

TextFilter

Se ubica en el package org.uqbar.arena.widgets. Permite interceptar valores ingresados a un campo textbox.

La interfaz define un solo método:

  • override boolean accept(TextInputEvent event): aquí hay que indicar con true/false si se acepta el caracter ingresado por teclado.

Ejemplo 1: DateTextFilter

Ejemplo 2: un filtro que sólo permite números, comas y puntos decimales (el que usa el campo NumericField)

Se usa de la siguiente manera.

Adapters

Property Adapter

Los selectores o combos necesitan trabajar con un objeto Adapter, para poder mostrar adecuadamente los elementos que contienen:

Lo que hacemos en el ejemplo de arriba es definir un selector o combo que muestra objetos Modelo de celular. Pero ¿qué propiedad usará para mostrar cada uno de los modelos de celular disponibles? Ahí es donde le decimos que utilice un adapter contra la propiedad descripcionEntera, definida en la clase Modelo:

Eso produce que en el combo se muestre la descripción del modelo y su precio en $:

Transformers para columnas de una grilla

Las tablas o grillas tienen en cada columna un binding con la propiedad que están observando:

No obstante, en algunos casos, es necesario adaptar la vista para mejorar la información que visualiza el usuario:

Aquí vemos que en lugar de mostrar "true" o "false", adaptamos el valor booleano a un string "SI"o "NO" respectivo:

Este otro ejemplo muestra cómo mostrar una columna con colores diferentes, donde true implica verde y false implica rojo:

Y se visualiza de esta manera:

La interfaz que estamos implementando es com.uqbar.commons.collections.Tranformer<T,U> (del package uqbar-commons-collections) :