Clase 1

Introducción

  • El objetivo de esta unidad es incorporar en una aplicación web algunos de los conceptos de modelado de interfaces de usuario que vimos en la unidad 2, en particular:
    • componentes,
    • eventos y binding.
    • una metáfora de navegación más apropiada para la construcción de aplicaciones (en contraposición a la metáfora tradicional de la web, basada en páginas y links).
  • Como base para ver esto vamos a utilizar wicket.
  • Lateralmente el wicket nos va a servir para ver dos conceptos que también son interesantes:
    • Convention over configuration
    • Estrategias para vincular la estética de una aplicación (diseño gráfico, html, css) con el comportamiendo de la aplicación (programación y diseño de sistemas, java, etc).

Primeros pasos con Wicket

  • Para comenzar debemos crear un proyecto Wicket desde cero que nos va a servir para mostrar algunos ejemplos básicos.
  • Desde el eclipse, podemos ver las diferentes partes del proyecto que nos creó:
      • Application, el objeto que representa a la aplicación.
      • HomePage, el objeto que representa a la página principal de la aplicación.
      • Html, el html de esa página (veremos que siempre van de a pares, html y objeto Java).
    • Aunque no es lo central a nuestra clase, podemos mirar también un par de chiches que tiene:
      • Start (es un chiche)
      • TestHomePage (se puede testear!)
      • log4j.properties (es la única configuración que tiene -> convention over configuration)
  • Analicemos lo que hace:
    • El html tiene un tag indentificado por un atributo especial wicket-id, eso configura un "hueco" que se va a completar con información dinámica.
    • No hay lenguaje de templates ni scriptlets, la única intrusión al html es ese id, se supone que eso permite que lo escriba directamente un diseñador. Se puede ver que el html funciona aún sin el wicket, eso es algo saludable para tener un preview.
    • Es orientado a componentes, cada parte dinámica de la página tendrá un componente asociado; en este caso se usa un Label.
    • Los componentes se deben crear en el momento de construcción de la página (en el constructor o en algún método que se invoque desde ahí).
    • Todo componente recibe un id por parámetro, que lo permite vincularse con el componente con el mismo id en el html (puede ser cualquier pedazo de html, creo).
    • El html y el java se asocian simplemente por su nombre, otro ejemplo de convention over configuration.
  • A continuación, podemos hacer un par de modificaciones, para familiarizarnos con los conceptos:
    • Modificar la clase HomePage, cambiarle el texto.
    • Agregar otro label... hay que tocar en dos lugares (podría considerarse una desventaja de la estrategia de wicket).
    • Si se agrega el label sólo al html se produce un error y se puede analizar la página de error que es bastante inteligente por la cantidad de info que tiene.

Comportamiento dinámico

A continuación vamos a agregar algunos links y algo de comportamiento dinámico a nuestra página.

  • Modificamos el html, agregar un link y otro label.
    • <a href="#" wicket:id="counterLink">This link</a> has been clicked
    • <span wicket:id="counter">123</span> times.
  • Agregar un label y un link al HomePage
  • Para crear el link hay que hacer una inner class... wicket usa mucho eso.
  • ¿Qué tiene que hacer el link?
    • No puede ser más fácil, sólo actualizar el contador: counter++;
    • Ahí se nota la ventaja de tener estado, no hay que ir a buscar el contador a ningún session ni request ni nada raro.
    • Y también la ventaja de programar la lógica separado del "rendering".
  • ¿Y el label cómo se entera de eso?
    • Para eso usamos "modelos", en SWT ya vimos algo de esto. La idea es modelar mi aplicación con objetos (modelo) y luego los componentes gráficos se asocian a esos objetos.
    • En este caso usamos un PropertyModel, es decir este modelo vincula a un componente con una propiedad de nuestros objetos. Después vamos a ver otras formas de modelo.
    • Nótese que usamos la palabra modelo ambiguamente... para nosotros el modelo son los objetos subyacente, para wicket Model son objetos que tienen una interfaz en común y representan el binding entre los componentes visuales y lo que yo llamo "modelo". (A no confundirse!).
    • Podría hablar directamente con el Label, pero nuestra propuesta es modelar el comportamiento y luego hacer que los componentes reflejen el estado de nuestro modelo. De esta forma el Label se mantiene siempre actualizado y yo puedo definir el comportamiento de la aplicación en términos de mi modelo y no de componentes visuales.
    • En este ejemplo está un poco mezclado porque el modelo está en la propia home page, cuando hagamos ejemplos más complicados lo vamos a sacar de ahí.
    • En este momento debemos detenernos un segundo para asegurarnos de entender todos lo que significa un modelo y qué rol juega un PropertyModel, relacionando las tres tecnologías que ya conocemos.
  • El hecho de que el contador sea una variable de instancia, nos hace ver que
    • Todos los pedidos le van al mismo objeto HomePage.
    • Cada usuario que ingresa al sistema tiene su propia instancia de HomePage.
    • Notar que el href del link en el html no es significativo, eso sirve (de nuevo) sólo para que la versión estática sea navegable si uno quisiera.
  • El código de todo esto queda:
    • add(new Link<Object>("counterLink") {
    • @Override
    • public void onClick() {
    • HomePage.this.counter++;
    • }
    • });
    • add(new Label("counter", new PropertyModel<Integer>(this, "counter")));

Primeros pasos con AJAX

  • Aunque hay cosas que uno podría criticar, vamos a ver que usar Ajax sobre wicket es claramente más simple que hacerlo a mano.
  • Primero cambiar el tipo de botón, usar AjaxLink.
  • Hasta ahí parece fácil... el detalle es que hay que avisar qué componentes hay que actualizar, para eso el onClick ahora recibe un objeto AjaxRequestTarget, al que le podemos indicar los componentes que deben ser actualizados vía Ajax. Eso se hace simplemente:
    • target.addComponent(... ¿qué va acá? ... );
      • Otro detalle adicional es que tener una referencia al label. En lugar de sólo crearlo, hay que guardarlo como una variable de instancia de HomePage (antes de que alguien pregunte, esto no tiene una penalización de performance; el objeto se guarda de todas maneras, solamente agregué una referencia).
      • Y un detalle más. Para las actualizaciones ajax se usa el id del componente (que no es el mismo que el wicket:id que les pusimos). Entonces tenemos dos opciones: o agregarle el id a mano al componente o indicar
        • this.counterLabel.setOutputMarkupId(true);
        • para que genere uno automáticamente.
  • Al igual que con todos los pedidos AJAX, podemos ver que al clickear el link ahora la URL no cambia.
  • Otro detalle es que si le das F5 crea una página nueva (nos damos cuenta porque se resetea el contador). Eso nos da una pauta del manejo de estado de wicket: mientras yo use la navegación propia de la página siempre voy a tener el mismo contexto de trabajo. Si utilizo la URL del browser eso me va a generar un contexto nuevo.
  • El código final queda:
    • add(new AjaxLink<Object>("counterLink") {
    • @Override
    • public void onClick(AjaxRequestTarget target) {
    • HomePage.this.counter++;
    • target.addComponent(HomePage.this.counterLabel);
    • }
    • });
    • this.counterLabel = new Label("counter", new PropertyModel<Integer>(this, "counter"));
    • this.counterLabel.setOutputMarkupId(true);
    • add(this.counterLabel);
  • Si en lugar de AjaxLink usás AjaxFallbackLink, tenés un link que usa Ajax donde puede y requests normales donde no. Eso serviría para browsers sin javascritp (existe eso todavía?) o por cuestiones de accesibilidad (supuestamente en USA es obligatorio).
  • Como resumen final... no hay una sola línea de JavaScript.

Formularios

  • Con lo que sabemos hasta aquí, podemos retomar el ejemplo de la calculadora
  • Comenzamos con el siguiente html, un form común y corriente.
    • <html>
    • <head><title>Calculadora</title></head>
    • <body>
    • <h1>Echo example</h1>
    • <form wicket:id="calculator">
    • <input wicket:id="op1" type="text" />
    • <input wicket:id="op2" type="text" />
    • <input wicket:id="calcular" type="submit" value="Sumar!" />
    • </form>
    • <p wicket:id="result">Fun Fun Fun</p>
    • </body>
    • </html>
    • Nuevamente vemos que los únicos agregados son los wicket:id
  • Creamos una WebPage con el mismo nombre que el html, qué hay que agregar ahí:
    • Naturalmente dos TextField para los operandos y un Label para el resultado.
    • Además hay que agregar un Form.
      • Esta decisión de la gente de wicket es discutible, dado que un form es una característica bien propia de la programación web y (en principio) no tendría por qué formar parte de una metáfora de más alto nivel.
      • Vemos entonces que el wicket nos provee de una metáfora intermedia, en la que nos permite incorporar conceptos de componentes, eventos, binding, pero también mantiene conceptos vestigiales de la programación web subyacente.
  • Llegamos entonces a la situación de preguntarnos cómo manejamos la información y el comportamiento de la calculadora.
    • Nos gustaría tener un modelo, al que llamamos Calculadora.
    • Tiene tres atributos enteros (dos operandos y un resultado) y un método "calcular".
    • Como siempre, podemos destacar que el modelo no sabe nada de wicket.
    • En este caso (a diferencia de SWT) no es necesario tirar eventos, pero no nos molestarían si estuvieran, entonces tranquilamente un modelo hecho para una aplicación SWT nos podría servir para nuestra aplicación wicket.
  • Ahora que tenemos un objeto modelo, todos los "Model" de todos nuestros componentes deberían pegar contra la calculadora.
    • El label es parecido a lo que ya hicimos.
    • Podríamos hacer lo mismo para los text fields, pero más piola es ponerle al form un CompountPropertyModel y dejar que los textfields lo usen (para eso se utilizan los nombres de cada componente).
    • Para pensar... ¿Por qué no funcionaría eso para el Label?
  • El onSubmit es muy fácil
    • @Override
    • public void onSubmit() {
    • FormExample.this.calculadora.calcular();
    • }
    • Para eso deberíamos tener un atributo calculator en la página, si no queremos eso también hay otra forma:(Calculadora)
    • this.getForm().getModelObject()).calcular();
  • Si quisiéramos usar Ajax, como antes habría que indicarle los componentes que se modifican... ahí se ve la desventaja de no tener los eventos.
  • Importante ver que tenemos binding automático:
    • Los datos de los operandos se vuelcan en la calculadora sin que yo tenga que hacer nada.
    • El resultado es leído por el label también automáticamente.
    • Inclusive se ocupa de las transformaciones de String a int.
  • Lo que nos faltaría es linkear de la página inicial a esta.
    • Si fuera por probar nomás podríamos cambiar el link pero ya que estamos podemos aprender a hacer links entre páginas.
    • Para eso hay que agregar un link (sí, en los dos lugares) y en el onClick:
      • @Override
      • public void onClick() {
      • this.setResponsePage(FormExample.class);
      • }
    • Nótese que todo se hace a nivel objetos, eso es una ventaja importante.
    • Tal vez sería más interesante que devolviera un objeto en lugar de sólo la clase, eso nos daría más posibilidades de diseño.