Wicket - Clase 3

Ejemplos avanzados para la reutilización de código HTML

  • Reutilización
    • herencia
    • paneles
  • Bookmarkable Links
  • Attribute Modifiers

Reutilización por herencia

Tenemos
PadrePage.html
<html>
<head></head>
<body>
<wicket:child>Aca va el contenido de la subclase</wicket:child>
</body>

class PadrePage extends WebPage
    new() {
        this.add(new Label("titulo", "Bienvenido desde la superclase"))
    }

e HijoMenorPage que hereda de PadrePage

class HijoMenor extends PadrePage y 
    new() {
        this.add(new Label("saludo", "Hola mundo!"))
    }

En el html del HijoMenor escribimos
<html>
<head></head>
<body>
--- Todo lo anterior se ignora, es sólo para el diseñador
<wicket:extend>
<div style="....">
<span wicket:id="saludo">Un saludo</span>
</div>
</wicket:extend>
</body>

¿Qué aprendemos aquí? Que Wicket permite diseñar un template method de pantallas y reutilizar el html fácilmente.

Paneles: "Favor object composition over class inheritance"

¿Qué hago si necesito tener un componente que quiero mostrar n veces? 
Una película, un zombie, un jugador, etc. Lo trabajo mediante composición, a partir de Paneles.

Ejemplo: tenemos un Usuario con nombre (String), dni (String) y cuenta (un Enum). Tenemos una pantalla que muestra los datos del usuario: nombre y DNI. Además tenemos que seleccionar:  
  • tipo de cuenta: cuentas propias, cuentas de terceros de este banco, cuentas de otros bancos
  • número de cuenta: anidado con el tipo de cuenta
  • saldo: en $
  • link Darle plata!
La vista es un UsuarioPage (extends WebPage), que tiene como modelo un usuario envuelto en un CompoundPropertyModel. Para mostrar los datos de la cuenta creamos un 
val panelCuenta = new CuentaPanel("cuenta", usuario.cuenta)

En el html tenemos
<div wicket:id="cuenta">
    DETALLE DE CUENTA
</div>

CuentaPanel extiende de Panel y es muy similar a una Page (el único detalle lo veremos después que es un XAttributeModifier).
 
Puedo referenciar a la página haciendo this.page...
Incluso podríamos definir que sólo se use ese componente en ciertas ventanas a las que yo después le envíe determinados mensajes.

El html del Panel sólo necesita comenzar con:
<wicket:panel> (todo lo anterior no tiene sentido para Wicket, sí para un diseñador con Dreamweaver)
    <dl>
        <dt>Tipo:</dt>
        <dd><span wicket:id="...">Caja de ahorro</span></dd>
    </dl>
</wicket:panel>

Disgresión: se puede configurar en modo Producción para que se eliminen los wicket:id, wicket:panel, wicket:extend, etcétera.

Atravesando límites

Podemos acceder a controles que están dentro de un componente hijo: this.get("cuenta/numero") me permite acceder al componente desde el padre. Esto es desaconsejable porque si no empiezan a proliferar referencias entre padres e hijos. Casos donde puede ser útil:
  • refrescar componentes vía AJAX (en forma manual, conociendo los ids)
  • o hacer cosas genéricas que afectan a una serie de componentes.
También podríamos armar un componente genérico que escuche los componentes que necesito refrescar porque cambiaron.

Otra cosa que podemos hacer es
this.get("cuenta").replaceWith(new CuentaPanel("cuenta", usuario.cuenta))

que permite cambiar dinámicamente un componente por otro.

Algunos ejemplos: 
  • Tabs que son botones ajax que cambian un panel por otro
  • Una pantalla de Transferencia con combos anidados

Vemos la pantalla

Tipo:
Combo que muestra
  • entre mis cuentas
  • a terceros Citi
  • a otros bancos
  • PayPal

Cuenta destino: 
El combo de destino va a variar en base a lo que seleccioné en el combo (combos anidados)

Lo que no podemos hacer en el replaceWith es 
  • variar los controles visuales (ej: tenía un Label y ahora quiero un TextBox). Los componentes wicket se asocian con distintos controles en HTML y no son intercambiables.
  • agregar o dejar de visualizar elementos
Sí sirve para mostrar dinámicamente dos paneles
TODO: Implementar un ejemplo copado.

Bookmarkable LInks

Volvemos al ejemplo de las cuentas bancarias. Recordamos las URL de Grails vs. las de Wicket, que tienen números raros:
http://localhost:8080/misc-avanzados-ui-wicket-xtend/wicket/page?3&sessionId=38423462374a82342384

Ese link no se puede compartir fácilmente, mientras que en Grails era fácil identificar la acción de modificar el libro "Rayuela":
http://localhost:8080/libro-ui-grails/editarLibro/8
(se puede compartir el link a otro usuario).

En Wicket yo se qué botón de qué página apretaron porque tengo estado. No obstante puedo generar "bookmarkable links", o sea links que puedo anotarme como favorito:
http://localhost:8080/misc-avanzados-ui-wicket-xtend/wicket/bookmarkable/org.uqbar.ui.wicket.misc.paneles.UsuarioPage?dni=7.147.129

Para eso construimos un constructor específico:
new(PageParameters parameters) {
    this(RepositorioUsuario.instance.buscarPorDni(parameter.get("dni").toString))
}

1) El constructor vacío permite que lo usemos desde una página de wicket.
2) El constructor con parámetros permite que le pasemos el dni específico para buscar un modelo en base a ese DNI.

En el ejemplo vemos cómo se genera un a href que vaya a esa página:
...
val params = new PageParameters
params.add("dni", "7.147.129")
this.addChild(new BookmarkablePageLink("usuario13" , UsuarioPage, params))
...

Y eso permite que nosotros veamos los links y guardarlos en los favoritos del browser.

XAttribute Modifier

En CuentaPanel hay un
saldoLabel.addBehavior(new XAttributeModifier("class", 
        [Float saldo | if (saldo < 0) "saldoNegativo" else "" ]))

¿Qué hace? Tarea para el lector: cambia en html el componente dinámicamente. Le agrega al dd un class="saldoNegativo".