Enero de 2013, artículo original de Hawk Chen, Engineer, Potix Corporation
Un Richlet es un pequeño programa en Java que construye el interfaz de usuario mediante programación en Java, y esto es una alternativa a escribir ficheros ZUL que se sirvan bajo petición del usuario.
Cuando un usuario hace un apetición mediante una URL (request) que está mapeada en el Richletf, ZK Loader hace responsable al Richlet de la construcción de la interfaz de usuario.
Esto les da a los desarrolladores todo el poder a la hora de crear los componentes. La elección entre páginas ZUML y Richlets depende de su preferencia.
En este artículo, demostraremos cómo usar un Richlet creando una pequeña aplicación que consistirá en un buscador. Para una explicacíon más detallada ver: Richlet en la ZK Developer's Reference.
La aplicación de ejemplo que vamos a construir es un simple catálogo de coches.
Esta aplicación tiene 2 funciones principales
Escribir una palabra clave en el campo de texto, hacer click en el botón de buscar y mostrar los resultados de la búsqueda en la lista contigua.
Haciendo clic en uno de los coches de la lista, el área contigua mostrará los detalles del coche seleccionado, incluyendo el modelo, precio, descripción y vistra prévia.
Para implementar un Richlet, tu clase tiene que implementar el interfaz Richelt. A pesar de que, no es necesario implementarla tal cual. En vez de eso puedes extender GenericRichlet y solo sobreescribir el método Richlet.service(Page). Este método será el que se llame cuando se haga una petición al Richlet por parte del usuario.
public class SearchRichlet extends GenericRichlet {
@Override
public void service(Page page) throws Exception {
//...
}
@Override
public void init(RichletConfig config) {
//initialize resources, e.g. get initial parameters
}
@Override
public void destroy() {
//destroy resources
}
}
Para tener mejor control, puedes incluso implementar los métodos Richlet.init(RichletConfig) y Richlet.destroy() para inicializar y destruir cualquier cosa que necesite el Richlet para funcionar.
Una de las funciones princpales de un Richlet es crear la interfaz de usuario. Es recomendable empezar leyendo los conceptos básicos sobre intefaz de usuario en ZK antes de crearla mediante Java.
Por ejemplo, un Grid en ZUL acpeta columnas y Filas como hijos. Para más detalle mirar la documentación al respecto: ZK Component Reference
public class SearchRichlet extends GenericRichlet {
@Override
public void service(Page page) throws Exception {
Component rootComponent = buildUserInterface();
...
}
private Component buildUserInterface(){
//build search area
final Textbox keywordBox = new Textbox();
Button searchButton = new Button("Search");
searchButton.setImage("/img/search.png");
Hbox searchArea = new Hbox();
searchArea.setAlign("center");
searchArea.appendChild(new Label("Keyword:"));
searchArea.appendChild(keywordBox);
searchArea.appendChild(searchButton);
...
}
...
}
Línea 12: Para crear un componente, simplemente lo instanciamos.
Línea 14: Cada atributo de un componente tiene su correspondiente método setter que podemos usar para asignarle el valor correspondiente.
Línea 18: Para establecer una relación Padre-Hijo entre componentes, podemos usar el método appendChild(Component). Esto enlaza el componente indicado en el método, al final de la lista de componentes hijos (que ya tuviera) del componente padre. Hay otros métodos similares para hacer esto como setParent(Component), insertBefore(Component,Component). Puedes encontrar más información al respecto en la documentación
Después de proveer el objeto Modelo al componente ListBox, normalmente necesitamos indicar cómo mostrar los datos que contiene. Necesitamos crear un objeto Render para ello.
Un Render es una clase Java en la que se indica cómo se va a mostrar cada dato del modelo. Lo creamos implementando la interfaz de tipo Render que corresponda a cada componente, para un Listbox usaremos ListitemRenderer. En la ZK Developer's Reference están la del resto de componentes.
Item renderer de un Listbox
class CarRenderer implements ListitemRenderer<Car>{
@Override
public void render(Listitem listitem, Car car, int index) throws Exception {
listitem.appendChild(new Listcell(car.getModel()));
listitem.appendChild(new Listcell(car.getMake()));
Listcell priceCell = new Listcell();
priceCell.appendChild(new Label("$"));
priceCell.appendChild(new Label(car.getPrice().toString()));
listitem.appendChild(priceCell);
}
}
Despues de crear el Render necesitamos asignarlo al ListBox.
carListbox.setItemRenderer(new CarRenderer());
En nuestra aplicacíon de ejemplo, un usuario puede hacer click en el botón de "Search" para realizar su búsqueda, tenemos por lo tanto que escuchar el evento "onClick". Podemos resolverlo invocando al método AbstractComponent.addEventListener(String, EventListener) del comonente. El primer parámetro es el nombre del evento, y el segundo es un objeto que implmente la interfaz EventListener. En un EventListener, puedes manipular los componentes para añadir tu lógica de la aplicación, como puede ser cambiar atributos de otros componentes, crear nuevos componentes, o eliminar uno existente. Puedes ver todas las características de los componentes en la ZK Component Reference, y ver qué atributos puedes necesitar usar.
public class SearchRichlet extends GenericRichlet {
private Component buildUserInterface(){
...
searchButton.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
//search
@Override
public void onEvent(Event event) throws Exception {
String keyword = keywordBox.getValue();
List<Car> result = carService.search(keyword);
carListbox.setModel(new ListModelList<Car>(result));
}
});
...
}
}
Línea 7: Puedes crear una clase a parte que implemente EventListener. En este ejemplo usamos una clase anónima por simplificar. Pero ojo, en un clúster debes implementar la versión serializable SerializableEventListener.
Línea 10: Escribe la lógica de tu aplicación dentro del método onEvent(), cosas como leer un valor con un get, cambiar un atributo de un componente o actualizar información
Otra función del ejemplo es que cuando el usuario selecciona un coche de la lista, mostramos sus detalles en el área de información. Para manejar esto, tenemos que escuchar el evento de seleccionar del componente Listbox, y entonces leer los atributos del coche y asignar los valores a los componentes correspondientes de la vista.
public class SearchRichlet extends GenericRichlet {
private Component buildUserInterface(){
...
carListbox.addEventListener(Events.ON_SELECT, new EventListener<SelectEvent>() {
//show selected item's detail
@Override
public void onEvent(SelectEvent event) throws Exception {
//get selection from listbox's model
Set<Car> selection = ((Selectable<Car>)carListbox.getModel()).getSelection();
if (selection!=null && !selection.isEmpty()){
Car selected = selection.iterator().next();
previewImage.setSrc(selected.getPreview());
modelLabel.setValue(selected.getModel());
makeLabel.setValue(selected.getMake());
priceLabel.setValue(selected.getPrice().toString());
descriptionLabel.setValue(selected.getDescription());
}
}
});
...
}
}
Hay 2 requisitos para que el Richlet esté disponible para la aplicación cliente.
Por defecto, los richlets están desactivados. Para activarlos, hay que añadir la siguiente declaracíon en el fichero WEB-INF/web.xml.
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>/zk/*</url-pattern>
</servlet-mapping>
De este ejemplo, puedes cambiar /zk/* a otro patrón (que corresponda con una URL válida) que tu quieras, como por ejemplo /do/*. Fíjate que no puedes mapear la expresión como si de una extensión de un fichero se tratase (como por ejemplo *.do) o se procesará como una página ZUML en vez de como un Richlet.
Cada Richlet que implementes, debes declararlo en la configuración WEB-INF/zk.xml de la siguiente forma:
<richlet>
<richlet-name>SearchRichlet</richlet-name>
<richlet-class>tutorial.richlet.SearchRichlet</richlet-class>
</richlet>
<richlet-mapping>
<richlet-name>SearchRichlet</richlet-name>
<url-pattern>/search</url-pattern>
</richlet-mapping>
Línea 6: Después de definir el Richlet, puedes mapearlo bajo tantas URLs como quieras mediante el elemento de configuración richlet-mapping
Puedes visitar http://localhost:8080/PROJECT_NAME/zk/search y ver tu Richlet. La URL especificada en el parámetro url-pattern debe empezar siempre con "/".
Si la URL termina en /*, se asocia a cualquier petición con el mismo prefijo. Para recibir la URL de la petición actual puedes chequear el valor que retorna el método getRequestPath de la página en la que te encuentres.
public class MyRichlet extends GenericRichlet {
@Override
public void service(Page page) throws Exception {
if ("/search/admin".equals(page.getRequestPath())){
//build admin UI
}else{
//build normal UI
}
...
}
}
ZK es tan versátil que provee de multiples opciones para que seas tú quien elija cómo construir la Interfaz de usuario, o incluso varias al mismo tiempo, dependiendo de tus preferencias o entorno.
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