Desarrollo de UI con componentes: binding avanzado @Deprecated

Binding avanzado de propiedades

Usamos como base el ejemplo datos básicos de empleados.
Armamos el layout de la pantalla. Necesitamos:
  1. Un combo para mostrar los empleados
  2. Varios labels o controles textbox que no sean editables
¿Cuál es el modelo del panel?
Vamos a trabajar con un modelo de vista específico (un objeto Application Model) DatosBasicosEmpleadoModel que tenga el empleadoSeleccionado. 
Al armar el selector/combo de empleados, sabemos que:
  1. la lista de empleados define el conjunto de opciones del combo
  2. la propiedad selected del combo está bindeada al empleado seleccionado
El formulario se completa con:
  • fecha de ingreso: text box read-only
  • antiguedad: text box read-only, se visualiza en rojo si tien
  • tiene hijos: check box read-only
Veamos a continuación cómo generar el text box no-editable para la fecha de ingreso:    
Text textFechaIngreso = formPanel.addText(DatosBasicosEmpleadoModel.CAMPO_FECHA_INGRESO, "Fecha de ingreso");
textFechaIngreso.setEditable(false);

Algunas diferencias con la versión 1.0 de Arena respecto a la actual:
  1. Cuando hacemos addText, nos pide dos parámetros: el primero es la propiedad contra la que se bindea el value del textbox (por default toma el model de la ventana, en nuestro caso el DatosBasicosEmpleadoModel) y el segundo parámetro es una descripción para crear un Label automático a la izquierda del Textbox. La nueva versión de Arena es menos declarativa y menos directa, necesitamos más líneas para decir lo mismo. A nuestro favor está claro que tenemos mayor control, podemos generar o no el Label.
  2. El addText devuelve el widget de SWT: esto es un tanto controversial porque
    1. por un lado Arena debería abstraernos de los detalles de implementación pero nos devuelve un objeto dependiente de la tecnología (no es cualquier Text, es un Text de SWT)
    2. muchas veces nos pasa que queremos customizar un control, acceder a ciertas propiedades, y si el framework no las publica no tendremos esa posibilidad. Es lo que justamente sucede en este ejemplo, donde necesitamos decirle al textbox que no es editable.  Distinguimos el hecho de asignar setEditable(false) del binding, porque mientras en el binding el atributo define dinámicamente qué pasa con la propiedad del control, la asignación es "manual" por parte del programador.
La misma técnica se aplica para el textbox antigüedad y el check tiene hijos, aunque el checkbox y el textbox necesitan mensajes diferentes.

Por último, necesitamos generar un binding entre la propiedad visible del textbox fecha de ingreso, y el color con el que mostraremos la antigüedad. Eso se modela con un binder de JFaces, dentro del mismo método describeForm:

formPanel.getBinder().bind(this.getModel(), UIProperty.VISIBLE, DatosBasicosEmpleadoModel.CAMPO_TIENE_HIJOS, textFechaIngreso);
formPanel.getBinder().bind(this.getModel(), UIProperty.FOREGROUND, DatosBasicosEmpleadoModel.CAMPO_COLOR_EMPLEADO, textAntiguedad);
    
Vemos el modelo del Panel:
  • el empleado seleccionado es una propiedad que tiene getter y setter, porque cada vez que el usuario modifique la opción seleccionada en el combo hay que disparar la notificación al modelo (enviando un mensaje setEmpleadoSeleccionado)
  • las otras propiedades bindeadas a controles de la vista son read-only, por lo tanto no tienen setters, sólo getters. Y no necesitamos tener un atributo en el modelo (no hace falta definirla como variable private)
  • el setter setEmpleadoSeleccionado tiene que disparar los cambios de las demás propiedades para que la vista actualice la fecha de ingreso, la antigüedad y la cantidad de hijos. 
public void setEmpleadoSeleccionado(Empleado empleadoSeleccionado) {
Object oldColorEmpleado = this.getColorEmpleado();
Empleado oldEmpleadoSeleccionado = this.getEmpleadoSeleccionado();
this.empleadoSeleccionado = empleadoSeleccionado;
this.firePropertyChange(CAMPO_EMPLEADO_SELECCIONADO, oldEmpleadoSeleccionado, this.empleadoSeleccionado);
this.firePropertyChange(CAMPO_EMPLEADO_NOMBRE_SELECCIONADO, oldEmpleadoSeleccionado.getNombre(), this.empleadoSeleccionado.getNombre());
this.firePropertyChange(CAMPO_ANTIGUEDAD, oldEmpleadoSeleccionado.getAntiguedad(), this.empleadoSeleccionado.getAntiguedad());
this.firePropertyChange(CAMPO_FECHA_INGRESO, "", this.empleadoSeleccionado.getFechaIngreso());
this.firePropertyChange(CAMPO_TIENE_HIJOS, !this.empleadoSeleccionado.tieneHijos(), this.empleadoSeleccionado.tieneHijos());
this.firePropertyChange(CAMPO_COLOR_EMPLEADO, oldColorEmpleado, this.getColorEmpleado());
}


    Algunos getters sirven también como adapters del modelo a strings para los texts:
    public String getEmpleadoNombreSeleccionado() {
        return this.empleadoSeleccionado.toString();
    }

    public String getFechaIngreso() {
        return new SimpleDateFormat("dd/MM/yyyy").format(this.empleadoSeleccionado.getFechaIngreso());
    }


    El color se obtiene delegando en parte a métodos del negocio:
    public Object getColorEmpleado() {
        if (this.empleadoSeleccionado.getAntiguedad() > 5) {
            return new Color(null, 0, 0, 255);
        } else {
            return new Color(null, 255, 50, 0);
        }
    }

Fíjense que la antigüedad es responsabilidad del empleado, pero el empleado no maneja el concepto del color, entonces esa responsabilidad la tiene el modelo de la vista.

Y lo otro es automágico: cada vez que cambia la propiedad "tieneHijos" (esto es, cuando isTieneHijos pasa de false a true o viceversa) aparece o desaparece el control fecha de ingreso. Lo mismo ocurre con el color de la antigüedad: no hay que programar el if, hay que programar el binding. Esto es un cambio de paradigma para desarrollar interfaces de usuario.

Estos tres métodos refuerzan el hecho de tener un app model propio y no un empleado (de otra manera ensuciaríamos el modelo con temas que no son específicos del negocio)
Comments