Desarrollo Web con componentes: Componentes en Wicket

Segundo ejemplo de Wicket: Calculadora

El ejemplo lo pueden encontrar acá.

Veamos ahora la división según MVC:

 Model: Calculadora.java Controller: CalculadoraPage.java View: CalculadoraPage.html
  estado interno: tiene una Calculadora 
  form: "calculadoraForm" FORM wicket:id="calculadoraForm"
double operando1 TextField: "operando1"          INPUT type=text wicket:id=operando1
double operando2 TextField: "operando2"      INPUT type=text wicket:id=operando2 
double sumar() Button: "sumar"      INPUT type=submit wicket:id=sumar
String getResultado() Label: "resultado"      SPAN wicket:id=resultado
  Link      A HREF

  1. El usuario solicita la página: antes de renderizar la página html al cliente ya tenemos nuestro controller con:
    1. un objeto calculadora instanciado (operando1 y operando2 en 0.0)
    2. textfields asociados a operando1 y operando2
    3. un button asociado al submit de sumar
  2. En el cliente se visualiza 0 en ambos operandos
  3. El usuario ingresa 7 en el operando1, 9 en el operando2 y presiona el botón Sumar
  4. Se submite el formulario al Server, entonces se acciona el controller. El form de wicket setea al model todas las propiedades al model:
    1. en el operando1 de Calculadora se asignó 7.0 (setOperando1)
    2. en el operando2 de Calculadora se asignó 9.0 (setOperando2)
  5. Se envía el mensaje sumar() al objeto calculadora que es modelo de dominio
  6. Al sumar se actualizan los operandos 1 y 2
  7. Se refresca la página, entonces se llena la información del form html nuevamente (con la información de la calculadora.java):
    1. en el input type text operando1 se setea el valor de operando1 (getOperando1)
    2. en el input type text operando1 se setea el valor de operando2 (getOperando2)
    3. en el span resultado se setea el valor de resultado (método String getResultado())
  • No hace falta que la calculadora mantenga un estado para el resultado, basta con que exista un método getResultado (es una property read-only).
  • Como vemos hay un "maridaje" en la asociación Controller Wicket-View HTML: 
    • Form-Form (!) 
    • Label-Span, 
    • TextField-Input Type Text
    • ListView-Tabla HTML (como pueden ver en el mismo ejemplo de la búsqueda de libros)
    • en general hay que respetar la misma jerarquía del HTML, eso lo vuelve poco flexible (no es transparente el rediseño de la vista)
  • Cambiar de input type=submit a input type=button produce que los controles no reciban los valores que cargó el usuario en pantalla (no viajan por el request, pruébenlo: los operandos quedan en cero). Este es un incoveniente propio del protocolo, que lamentablemente Wicket no esconde (la metáfora tiene un límite).
  • Por otra parte, si se carga un operando con valores alfabéticos solito el wicket te muestra el error en formato de viñetas.
  • Cada control observa la propiedad del modelo, esto es bueno porque no tengo que encargarme de codificar el binding: basta con seguir la convención de darle el mismo nombre en la vista html, en el controller de wicket y en el modelo de dominio (como pasa por ejemplo con el operando1 y operando2). Pero la actualización no es instantánea, esto sólo puede ocurrir cuando el usuario haga submit del form.
  • Se puede debuggear para ver que al llamar a sumar() cada control observa la propiedad del modelo y en consecuencia se refresca.

Números aleatorios

Este proyecto es numerosRandom-ui-wicket y tiene el siguiente layout:



Tenemos 3 componentes donde cada uno consiste en 
  • un label y 
  • un botón que permite generar números random en un rango determinado
La vista no va a generar los números aleatorios, eso se lo vamos a dejar a un modelo. ¿Qué necesitamos que tenga ese modelo?
  • El último número que salió (porque lo vamos a mostrar en pantalla)
  • El rango de números (cota inferior/superior) sobre el cual vamos a elegir el número al azar
  • Un método que genere ese número al azar
Entonces:
 Vista: HomePage.html Controller: HomePage.java Model?
DIV wicket:id="random" RandomComponent: "random" no hay...
DIV wicket:id="random2"  RandomComponent: "random2" 
DIV wicket:id="random3" RandomComponent: "random3" 

Es interesante notar que el HomePage actúa de container, generando un Composite de controles. 
No hay un model aún, porque vamos a trabajar más granularmente, a nivel componente:

 Vista: RandomComponent.html Controller: RandomComponent.java Model: GeneradorRandom.java
 SPAN wicket:id=resultado Label: resultado (seteamos el output markupId para trabajarlo con ajax) getter int resultado (que devuelve el valor de la variable). La propiedad es read-only, no hay setter.
 FORM wicket:id=form Form: "form" 
      INPUT TYPE=submit wicket:id=boton      AjaxButton: "boton" método generar() que actualiza resultado

Trabajar con componentes nos permite 
  • concentrarnos en menos cosas a la vez
  • resolver un problema y aplicarlo en muchos lugares (búsqueda de usuarios, combos anidados, etc.)
  • no pensar en una ventana como un todo sino como un conjunto de partes relacionadas (aumenta la granularidad)
  • lo bueno es que Wicket conserva un estado propio para cada componente (cada uno tiene su propio GeneradorRandom, los números aleatorios que genera nunca se cruzan: el primero va de 20 a 70, el segundo de 1 a 10 y el tercero de 70 a 170)

Búsqueda de libros

Volvemos al proyecto ejemplosBasicos-ui-wicket para ver cómo se trabaja una grilla en Wicket.
  • Una grilla es un componente (como vimos recién)
  • La consulta la queremos hacer por ajax con un remote link
  • Aparece el form como otro container de controles 
¿Cómo relaciono cada columna de la grilla?

 Vista: BusquedaLibros.html Controller: BusquedaLibros.java 
     Modelo: BuscadorLibros 
 Application Model: BuscadorLibros
 TABLE con dos th  
     TR wicket:id=resultado Component panelResultado = PropertyListView("resultado")   
 redefine un método populateItem(ListItem<Libro> item)
 property resultado que se lee y se actualiza (cuando el usuario dispara la consulta se ejecuta el método buscar()). El resultado es un List<Libro>
          en un TD, un SPAN wicket:id=titulo     se agrega a listItem un Label "titulo"    se asocia a la propiedad titulo del libro 
          en otro TD, un SPAN wicket:id=autor     se agrega a listItem un Label "autor"     se asocia a la propiedad autor del libro
  el panelResultado se incorpora al form que es container principal