Tu aplicación web ZK enfoque MVVM (5-5)
Framework ZK
Tu aplicación web ZK enfoque MVVM (5-5)
Contenido
- Introducción
- Automatizando la Interfaz de usuario
- Abstrayendonos de la vista
- Asociando la interfaz de usuario con el “ViewModel”
- Mostrando un Conjunto de Datos
- Implementando la Funcionalidad de "Ver Detalles"
- Importar y Ejecutar la Aplicación de Ejemplo
- Referencias
Introducción
Este tutorial pertenece a la siguiente serie:
- Configurar el entorno
- Empezar a programar
- MVC y MVVM ¿Qué son y en qué se diferencian?
- MVC en ZK
- MVVM en ZK
Está dirigido a desarrolladores con experiencia en la creación de aplicaciones web en Java. A continuación aprenderás a manejar los componentes de la vista automáticamente e implementar la lógica necesaria para que funcione nuestra aplicación.
A lo largo de este documento hacemos referencias a fuentes externas, las encontrarás todas en la sección de Referencias
Automatizando la Interfaz de usuario
El enfoque que introducimos en esta parte es que ZK controle por tí la interacción del usuario sobre los componentes de la vista. A esta manera de proceder se la identifica como patrón de diseño Modelo-Vista-VistaModelo (MVVM). [2]
Este patrón divide la aplicación en 3 partes.
El Modelo consta de los datos de la aplicación y las reglas de negocio. La clase CarService y otras clases usadas por ella representan la parte del modelo en nuestra aplicación de ejemplo.
La Vista cubre la interfaz de usuario. La página zul que contiene los componentes de ZK representa esta parte. La interacción del usuario con los componentes de la vista genera eventos que son enviados a los controladores.
La VistaModelo es la responsable de exponer la información del Modelo a la Vista y procesar las acciones que la vista requiera cuando el usuario interactúe con ella. La VistaModelo se entiende como una abstracción de la Vista, que contiene el estado de la misma y controla su comportamiento. Pero la capa VistaModelo no debe contener referencias a los componentes de la interfaz de usuario. Es ZK quien controla la comunicación y sincronización entre la Vista y la VistaModelo.
Por lo tanto, bajo este enfoque, lo único que preparamos es una clase VistaModelo con los setters, getters y métodos que necesitemos, después los asignamos mediante las expresiones de “data binding” a los atributos de los componentes del zul (nuestra vista). Hay un “binder” en ZK que sincroniza los datos entre la capa VistaModelo y los componentes de la vista automáticamente, en función de lo que definamos en las “binding expressions”. Por lo tanto, no es necesario que nosotros controlemos los componentes.
A continuación te mostramos como funciona MVVM en ZK con la función de búsqueda de nuestra aplicación. Asumimos que el usuario ha hecho clic sobre el botón “Search”, entonces el “listbox” actualizará su contenido. El flujo de ejecución es el siguiente:
- El usuario hace clic en el botón “Search” y el evento correspondiente es enviado.
- El “binder” de ZK invoca el correspondiente método en la capa VistaModelo.
- El método accede a la información de la capa Modelo y actualiza las propiedades necesarias de la capa VistaModelo.
- El “binder” de ZK lee las propiedades de la VistaModelo y actualiza el estado de los componentes (en este caso de la lista “listbox” y su contenido).
Abstrayendonos de la vista
La capa de VistaModelo es una abstración de la vista. Por lo tanto, cuando diseñamos la capa de VistaModelo, debemos analizar la funcionalidad de la interfaz de usuario (vista) para identificar el estado de los componentes y el comportamiento que esperamos de los mismos.
El estado:
- Texto que el usuario escribe
- Resultados de la búsqueda
- Coche seleccionado
El comportamiento:
- La acción de búsqueda
Deacuerdo con el análisis anterior, la capa de VistaModelo debe tener 3 variables (para los estados) y un método (para el comportamiento). En ZK, crear la capa de VistaModelo es como crear un POJO, y este nos presenta su estado como si de las propiedades de un JavaBean se tratase, a través de métodos setters y getters. El método que necesitamos para realizar la búsqueda implementa la lógica del modelo (es decir la llamada al objeto de servicio “carService”) y actualiza la propiedad “carList” correspondiente.
Extraído de SearchViewModel.java
package tutorial;
import java.util.List;
import org.zkoss.bind.annotation.*;
public class SearchViewModel {
private String keyword;
private List<Car> carList;
private Car selectedCar;
//omit getter and setter
public void search(){
carList = carService.search(keyword);
}
}
Anotación
En ZK MVVM, cualquier comportamiento que pueda requerir la Vista lo entendemos como “comando” en la capa de VistaModelo. Podemos enlazar un evento de un componente con un comando de la VistaModelo y ZK invocará el método cuando el evento que hemos enlazado sea lanzado desde la Vista. Para permitir que ZK conozca qué comportamiento (método) es el que queremos lanzar, debemos aplicar una anotación de tipo @Command sobre el método. Hemos marcado el método “search()” como comando con un nombre por defecto “search”, que es el mismo que el nombre del método. El nombre del comando es utilizado en la expresión que usamos para enlazar el evento con el método, hablaremos de esto más adelante.
En el método “search()”, cambiamos la propiedad “carLista” de la capa VistaModelo. Así pues debemos notificar a ZK que la propiedad ha sido cambiada, para ello usamos la anotación @NotifyChange, así pues ZK será capaz de recargar la propiedad después de que el método haya sido invocado.
Para el comando “search” sería así:
Extraído de SearchViewModel.java
package tutorial;
import java.util.List;
import org.zkoss.bind.annotation.*;
public class SearchViewModel {
//omit other codes
@Command
@NotifyChange("carList")
public void search(){
carList = carService.search(keyword);
}
}
Puedes ver el código fuente completo en la sección de Referencias [3].
Asociando la interfaz de usuario con el “ViewModel”
Bajo el patrón MVVM, nosotros construimos nuestra interfaz de usuario del mismo modo que lo hacemos con el patrón MVC, y es entonces cuando establecemos la relación entre la capa Vista (zul) y la capa VistaModelo mediante expresiones de “data binding” sobre los atributos de los componentes, y dejamos que ZK maneje el componente y sus eventos por nosotros.
Para asociar un componente a la capa VistaModelo, podemos aplicar un “composer” llamado “org.zkoss.bind.BindComposer”. Esta clase “composer” procesa las expresiones de “data binding” e inicializa las clases de la capa VistaModelo. Entonces asociamos el componente a la capa de VistaModelo estableciendo su atributo “viewModel” con la siguiente sintaxis:
@id('ID') @init('FULL.QUALIFIED.CLASSNAME')
- @id() es usado para establecer el id de la variable que queramos de la capa VistaModelo. Usaremos ese id para referencia las propiedades de la capa VistaModelo (por ejemplo vm.carList) con las expresiones “data binding”.
- Debemos proveer un nombre de clase completamente cualificado para @init() para inicializar el objeto de la capa VistaModelo.
Extraído de searchMvvm.zul
<window title="Search" width="600px" border="normal"
apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('tutorial.SearchViewModel')">
<!-- omit other tags-->
</window>
Después de asociar la capa VistaModelo con el componente, todos sus componentes hijos pueden acceder a las propiedades de la capa VistaModelo asociada al componente padre.
Por ejemplo, podemos asociar la capa de Vista con las propiedades y comportamiento de la capa VistaModelo con expresioens “data binding”. Veamos cómo usamos estas expresiones de “data binding” para lograr que la búsqueda funcione.
En cuanto hemos declarado variables en la clase de la capa de VistaModelo para los estados de los componentes en la sección anterior, podemos asociarlos a atributos de componentes de la vista. Después de asociar un atributo de un componente a la VistaModelo, ZK automáticamente sincronizará la información contenida en el atributo y la propiedad correspondiente en la capa de VistaModelo por nosotros.
Podemos especificar qué atributo es asociado a qué propiedad mediante una expresión “data binding” directamente en un atributo del componente de la capa de la Vsita con la sintaxis:
@bind(vm.aProperty)
- Recuerda que “vm” es el id que le dimos en la anotación @id() anteriormente y ahora lo usamos para referenciar desde la capa de la Vista el objeto de la capa VistaModelo.
La función de búsqueda tiene 2 estados relacionados con ella para ser almacenados en la capa de VistaModelo derivados del anterior análisis. Primero, queremos guardar el valor del “textbox” en la propiedad “keyword” de la capa de VistaModelo. Podemos asociar el atributo “value” del “textbox” a la propiedad del objeto de la capa VistaModelo “vm.keyword” simplemente con “@bind(vm.keyword)”. Segundo, queremos almacenar la información del modelo como una “listbox” en el objeto “carList” de la capa VistaModelo, por lo tanto debemos asociar el atributo “model” de “listbox” con el objeto “vm.carList”.
Extraído de searchMvvm.zul
<hbox>
Keyword:
<textbox value="@bind(vm.keyword)" />
<button label="Search" image="/img/search.png"/>
</hbox>
<listbox height="160px" model="@bind(vm.carList)" emptyMessage="No car found in the result">
<!-- omit other tags -->
Solo podemos asociar eventos de los componentes de la vista (por ejemplo onClick) a los métodos de comportamiento programados en la capa de VistaModelo. Después de asociar un evento a la capa de VistaModelo, cada vez que se lance el evento, ZK encuentra el método enlazado (de comando) y lo invoca. Por lo tanto para capturar el evento clic del botón “Search”, tenemos que asociar el evento onClick del componente a un comando (método) con la siguiente sintaxis:
@command('COMMAND_NAME')
El nombre del comando debe corresponder con el nombre del método de comando especificado en la capa de VistaModelo.
Extraído de searchMvvm.zul
<hbox>
Name Keyword:
<textbox value="@bind(vm.keyword)" />
<button label="Search" image="/img/search.png" onClick="@command('search')" />
</hbox>
<listbox height="160px" model="@bind(vm.carList)" emptyMessage="No car found in the result">
<!-- omit other tags -->
Después de asociar el evento “onClick”, cuando el usuario haga clic en el botón “Search”, ZK invocará el método “search()” de comando y recargará la propiedad “carList” puesto que la hemos indicado en la anotación: @NotifyChange.
Mostrando un Conjunto de Datos
La forma de mostrar una colección de datos mediante “data binding” es similar a la forma que usamos mediante el patrón MVC. Usaremos la etiqueta “<template>” [4], para controlar que se renderiza cada elemento. La única diferencia es que debemos usar una expresión “data binding” en vez del Expression Language (EL) que usábamos en MVC.Extraído de searchMvvm.zul
<listbox height="160px" model="@bind(vm.carList)" emptyMessage="No car found in the result">
<listhead>
<listheader label="Name" />
<listheader label="Company" />
<listheader label="Price" width="20%"/>
</listhead>
<template name="model">
<listitem>
<listcell label="@bind(each.name)"></listcell>
<listcell label="@bind(each.company)"></listcell>
<listcell>$<label value="@bind(each.price)" />
</listcell>
</listitem>
</template>
</listbox>
Pasos para usar “<template>”:
- Usar “<template>” para encapsular componentes que queremos repetir iterativamente.
- Establecemos el atributo “name” del “<template>” como “modelo” [5].
- Usamos la variable implícita del lenguaje zul “each” para asignar las propiedades del objeto de dominio “Car” a los correspondientes atributos del componente visual.
Implementando la Funcionalidad de "Ver Detalles"
Los pasos para implementar la función de ver detalles es similar al de las secciones anteriores.
Antes, hemos conectamos el atrubuto “selectedItem” del componente listbox a la propiedad “vm.selectedCar” para guardar el objeto de dominio seleccionado.
Ahora, queremos mostrar los detalles del “Car” seleccionado, por lo tanto conectaremos el atributo “label” y “src” de la imagen a la propiedad del objeto “Car”, que será accesible mediante la notación siguiente “vm.selectedCar.name”.
Cada vez que un usuario selecciona un “listitem”, es decir un elemento de la lista, ZK guarda el objeto “Car” seleccionado en la capa “ViewModel”. Entonces ZK recarga las propiedades del objeto “selectedCar” en los atributos conectados a ellas.
<listbox height="160px" model="@bind(vm.carList)" emptyMessage="No car found in the result"
selectedItem="@bind(vm.selectedCar)">
<!-- omit child components -->
</listbox>
<hbox style="margin-top:20px">
<image width="250px" src="@bind(vm.selectedCar.preview)" />
<vbox>
<label value="@bind(vm.selectedCar.name)" />
<label value="@bind(vm.selectedCar.company)" />
<label value="@bind(vm.selectedCar.price)" />
<label value="@bind(vm.selectedCar.description)" />
</vbox>
</hbox>
Para obtener el código fuente completo del fichero .zul, por favor mira la sección de referencias [6]
Importar y Ejecutar la Aplicación de Ejemplo
A continuación puedes descargar el código fuente completo de la aplicación de ejemplo, preparado sobre un proyecto de Ecplise que puedes importar directamente en él, de forma que no tengas que empezar desde 0.
Para usar la aplicación de ejemplo sigue los siguientes pasos:
- Descarga el zip que contiene la aplicación de ejemplo de Aquí
- En Eclipse, selecciona “File / Import / General / Existing Projects into Workspace”, y elige “Select archive file” para importar el fichero .zip que contiene el ejemplo como proyecto en tu Eclipse.
- Y sigue las instrucciones del tutorial de esta serie correspondiente a Configurar el entorno para arrancarlo.
Referencias
- [1]Página web oficial de ZK
- [2] MVVM, guía del desarrollador
- [3] SearchViewModel.java
- [4] Template / Plantilla
- [5] Template / Plantilla para un componente ListBox
- [6] searchMvvm.zul
Este documento es un extracto de la documentación oficial del Framework ZK, traducido y ampliado por Francisco Ferri. Colaborador de Potix (creadores del Framework ZK). Si quieres contactar con él puedes hacerlo en franferri@gmail.com, en twitter @franciscoferri o en LinkedIn
Reader Comments