Categorías del Tutorial

icono_twiter
Jose Manuel Sánchez Suárez

Consultor tecnológico de desarrollo de proyectos informáticos.

Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación

Somos expertos en Java/J2EE

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2010-04-20

Tutorial visitado 53.550 veces Descargar en PDF
Facelets en JSF 2: sistema de plantillas y componentes por composición.

Facelets en JSF 2: sistema de plantillas y componentes por composición.


0. índice de contenidos.


1. Introducción.

Ahora que ya conocemos las principales novedades de JSF 2 de la mano de Alejandro, en este tutorial vamos a realizar una introducción al uso de Facelets en JSF 2, el sistema de plantillas estándar de facto en la versión 1.2 de JSF y que ahora, en la versión 2, forma parte de la propia especificación.

Con JSF 1.2, la versión anterior, existía la posibilidad de utilizar otros sistemas de plantillas: Tiles de Apache (siempre más ligado al uso de Struts), JsfTemplating (asociado a Woodstock) o JTPL de IBM el sistema de plantillas estándar del entorno de desarrollo de IBM; si bien, para los que apostamos por facelets, al ver las múltiples ventajas frente a estos otros sistemas, ahora nos encontramos con que se soporta por defecto, puesto que ya forma parte del estándar y ya tenemos parte del camino recorrido. Es más:

  • ahora Facelets es el sistema por defecto para crear vistas basadas en árboles de componentes JSF, en detrimento de JSP, ya no hay que configurar el soporte de Facelets, al contrario, hay que habilitar el modo de compatibilidad con JSP, si queremos seguir usándolo.
  • ninguna de las nuevas características de JSF 2 (el soporte nativo para Ajax, la gestión de eventos del ciclo de vida desde la vista, por supuesto la nueva definición de componentes por composición,...) están disponibles usando JSP.

En resumen, quien no haya migrado sus vistas de JSP a Facelets, ahora se va a perder lo bueno de JSF 2.

Centrándonos en Facelets, a "grosso modo", en JSF 2:

  • hace uso del mismo sistema de plantillas que veníamos usando hasta ahora en JSF 1.2, y
  • mejora la creación de componentes por composición, reduciendo complejidad en su configuración y añadiendo rigidez en su parametrización, puesto que ahora vamos a tener la posibilidad de definir qué atributos acepta el componente por composición, además de exponer el comportamiento de los componentes que incluye. Esto no significa que los componentes por composición que ya tenemos creados, basándonos en JSF 1.2, los tengamos que re-escribir para JSF 2, porque son totalmente válidos.


2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 17' (2.93 GHz Intel Core 2 Duo, 4GB DDR3 SDRAM).
  • Sistema Operativo: Mac OS X Snow Leopard 10.6.1
  • JDK 1.6.0_17.
  • Maven 2.2.1.
  • Eclipse 3.5: Ganymede, con IAM (plugin para Maven).
  • Apache Tomcat 6.0.20.
  • JSF 2 (2.0.2)

3. El sistema de plantillas de Facelets.

Como he comentado en la introducción, el sistema de plantillas es el mismo que teníamos con JSF 1.2, y se basa en el uso de las siguientes etiquetas:

  • ui:composition: envuelve un conjunto de componentes para ser reutilizados en otra página, es la etiqueta que:
    • envuelve o puede envolver la plantilla.
    • se utiliza para hacer referencia a una plantilla.
    todo lo que quede fuera de la etiqueta ui:composition no será renderizado.
  • ui:define: define un contenido nombrado para ser insertado en una plantilla, su contenido es un conjunto de componentes y se identifica con un name. Ese conjunto de componentes será insertado en una etiqueta ui:insert con el mismo name.
  • ui:insert: declara un contenido nombrado que debe ser definido en otra página,
  • ui:decorate: es la etiqueta que sirve para hacer referencia a una plantilla, como la etiqueta ui:composition, solo que con ui:decorate lo definido antes y después de la etiqueta sí será renderizado,
  • ui:param: nos sirve para declarar parámetros y su valor en el uso de plantillas y componentes por composición,
  • ui:include: es una etiqueta que sirve para incluir en una página el contenido de otra, como si el contenido de esta última formase parte de la primera.


3.1. Definición de la plantilla de aplicación.

Siguiendo las recomendaciones de facelets, las plantillas las incluiremos dentro del directorio WEB-INF/facelets/templates/, de modo que no sean visibles desde el contenedor web.

Un ejemplo de plantilla, defaultLayout.xhtml, podría ser el siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
	 
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html">
	 
<h:head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <h:outputStylesheet library="css" name="style.css" />
  <title><ui:insert name="title" /></title>
</h:head>
<h:body>
	<div id="layout">		
		<div id="header">
			<div id="logo"></div>
			<ui:include src="menu.xhtml"/>
		</div>
	
		<div id="content">
			<ui:insert name="content">
				<span>Página en construcción</span>
			</ui:insert>
		</div>
			
		<div id="footer">
			<p><a href="#">©</a></p>
		</div>
	</div>
</h:body>
</html>

Definimos una maquetación por capas y el contenido de nuestro site en tres secciones: header, content y footer

Prevemos la inclusión de la definición externa de dos contenidos nombrados: title (línea 10) y content (línea 20), de modo que la página que haga uso de la plantilla debe definir tales contenidos. Si no los define podemos prever un valor por defecto, en el caso de content "Página en construcción".

Por último, hemos hecho una inclusión en la línea 16 del contenido de una mini-plantilla, que contendrá el menú de la aplicación.


3.2. Definición de la plantilla de menú.

La plantilla del menú la ubicamos en el mismo directorio que la plantilla de aplicación y podría tener el siguiente contenido:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:p="http://primefaces.prime.com.tr/ui">
	<ui:composition>
		<p:menubar>
			<p:submenu label="#{msg.menu_home}">
				<p:menuitem label="Home" url="#" />
			</p:submenu>
			<p:submenu label="#{msg.menu_items}">
				<p:menuitem label="#{msg.new}" url="#{menuBean.createItem}"></p:menuitem>
				<p:menuitem label="#{msg.list}" url="#{menuBean.listItem}"></p:menuitem>
			</p:submenu>
		</p:menubar>
	</ui:composition>
</html>

Estamos haciendo uso de un componente visual de la librería de componentes primefaces, que soporta JSF 2, que renderiza el contenido de un árbol jerárquico de items como un menú.


3.3. Uso de la plantilla.

Por último, las páginas que hagan uso de la plantilla tendrán un contenido similar al siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html">

<ui:composition template="/WEB-INF/facelets/templates/defaultLayout.xhtml">
	<ui:define name="title">
		<h:outputText value="#{msg.edit_of} #{msg.menu_items}" />
	</ui:define>
	<ui:define name="content">
		<h:form>
			<!-- contenido de la página-->
		</h:form>
	</ui:define>

</ui:composition>
</html>

De este modo, en el único punto en el que se define la estructura del site es en la plantilla y el código de las páginas queda centrado en el contenido individual de cada una de ellas y no nos repetimos - Please DRY: Don't Repeat Yourself -.


4. Componentes por composición.

Los componentes por composición son un tipo especial de plantillas que actúan como componentes. Un componente por composición es una pieza de código reutilizable que proporciona cierta funcionalidad y consiste en una colección de otros componentes que ya existen.

Con facelets, cualquier página xhtml puede convertirse en un componente por composición, haciendo uso de las siguientes etiquetas:

  • composite:interface: declara el contrato de uso del componente por composición.

  • composite:implementation: define la implementación del componente por composición. Si existe un composite:interface, debe existir su composite:implementation correspondiente.

  • composite:attribute: declara un atributo en el contrato del componente, en la interface. Es una etiqueta hija de composite:interface.

  • composite:insertChildren: sustituye a la etiqueta ui:insert que antes usábamos en el componente para insertar el conjunto de nodos hijos que se puede declarar dentro del etiquetado que hace uso del componente por composición. Se incluye dentro de la etiqueta composite:implementation.

  • composite:valueHolder: permite exponer las propiedades y eventos de los componentes que implementan la interfaz ValueHolder para que sean asignados desde el cliente que hace uso del componente por composición. Los componentes que implementan EditableValueHolder son todos aquellos que soportan el acceso a propiedades del lado de los ManagedBeans a través de Expression Language y la posibilidad de asignar a dicha vinculación un conversor (outputText, outputLabel, outputFormat,...)

  • composite:editableValueHolder: permite exponer las propiedades y eventos de los componentes que implementan la interfaz EditableValueHolder para que sean asignados desde el cliente que hace uso del componente por composición. EditableValueHolder es una extensión de ValueHolder que permite asociar al componente que la implementa validadores y eventos de cambio de valor (inputText, inputSecret, selectOneMenu, selectManyMenu, selectBooleanCheckBox,...).

  • composite:actionSource: permite exponer las propiedades y eventos de los componentes que implementan la interfaz ActionSource para que sean asignados desde el cliente que hace uso del componente por composición. Los componentes que implementan ActionSource son aquellos que soportan eventos de acción y listeners de eventos de acción (commandButton y commandLink).


4.1. Configuración.

JSF 2 deja abierta la posibilidad de configurar nuestros componentes por composición a través de la declaración de los mismos en una taglib e incluyéndola en el descriptor de despliegue web.xml. Si bien, además, haciendo uso del concepto de convención sobre configuración, nos hace transparente dicha configuración de modo que nos propone un prefijo de URI predefinida para hacer referencia a los mismos y el sufijo vendrá dado por el directorio en el que se encuentren los xhtml que implementan los componentes por composición.

Es tan sencillo, como crear nuestros componentes bajo una estructura de directorios components/autentia dentro de la carpeta de recursos (resources) para que, cuando se empaquete se incluya dentro del directorio WEB-INF o META-INF. De este modo todos los xhtml que contengan la declaración de componente serán incluidos en una librería de componentes cuya URI tendrá una parte fija (http://java.sun.com/jsf/composite/) y un sufijo que se corresponderá con la estructura de directorios en el que esté incluido (components/autentia). El nombre del componente será el nombre del fichero xhtml.

Así, podemos mantener la siguiente estructura de directorios:

JSF 2

e incluir el espacio de nombres de los mismos en nuestros xhtml de la siguiente forma:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
         xmlns:tnt="http://java.sun.com/jsf/composite/components/autentia">
...

4.2. Creación de un componente por composición.

Para definir un componente por composición en JSF 2, ahora se requiere la definición de la interfaz y la implementación. Qué mejor que mostrar un componente complejo para entenderlo, se trata de un componente de entrada de datos que encapsula la etiqueta, el propio componente de entrada y el componente de mensajes asociado al anterior.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
         xmlns:h="http://java.sun.com/jsf/html"
         xmlns:f="http://java.sun.com/jsf/core"
         xmlns:ui="http://java.sun.com/jsf/facelets"
         xmlns:composite="http://java.sun.com/jsf/composite">
 <h:head>

   <title>This will not be present in rendered output</title>

 </h:head>

 <h:body>
	
   <composite:interface displayName="Input text field with label, input and message components" >
      <composite:attribute name="entity" required="true" />
      <composite:attribute name="field"  required="true" />
      <composite:attribute name="label"  required="false" default="#{cc.attrs.entity.class.simpleName}.#{cc.attrs.field}" />
      <composite:attribute name="required" required="false" default="false" />
   </composite:interface>

   <composite:implementation>
      
   	<h:panelGroup id="containerOflabel">
		<h:outputLabel id="label" 
		for="#{cc.attrs.field}" value="#{msg[cc.attrs.label]}" />
		<ui:fragment rendered="#{cc.attrs.required}">
			<h:outputText value="(*)" styleClass="red"/>
		</ui:fragment>
		<h:outputText value=":" />
	</h:panelGroup>

	<h:inputText id="input" required="#{cc.attrs.required}"
		value="#{cc.attrs.entity[cc.attrs.field]}" >
		<f:ajax render="message" />
	</h:inputText>

	<h:message id="message" for="input" errorClass="errorMessages" />
   
   </composite:implementation>

 </h:body>

</html>

Ahora tenemos:

  • por un lado la definición de la interfaz en la que se pueden definir los parámetros que recibirá o puede recibir el componente incluidos en la etiqueta composite:interface, esto es bastante útil porque eliminamos la necesidad de incluir tediosos bloques de <c:if comprobando el contenido de los parámetros.
  • por otro lado, la propia implementación dentro de la etiqueta composite:implementation, que hará uso de los parámetros definidos en la intefaz: dentro de JSF 2 cc es una palabra reservada para hacer referencia al componente por composición y que permite acceder al valor de los atributos definidos en la interfaz.

Todo lo que no esté incluido dentro de estas dos etiquetas no será renderizado.


4.3. Uso de un componente por composición.

Para hacer uso de un componente por composición basta con uncluir el espacio de nombres con la URI que hemos comentado anteriormente:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
         xmlns:h="http://java.sun.com/jsf/html"
         xmlns:f="http://java.sun.com/jsf/core"
         xmlns:ui="http://java.sun.com/jsf/facelets"
         xmlns:tnt="http://java.sun.com/jsf/composite/components/autentia">
   <h:head>
     <title>Composite component example...
   </h:head>

   <h:body>
     <h:form>
       <tnt:inputText entity="#{beanTestView.item}" field="reference" required="true"/>
     </h:form>	    	
   </h:body>
   
</html>

4.4. Exponer las propiedades y comportamientos de los componentes de un componente por composición.

Adicionalmente podemos incluir dentro de la declaración de la interfaz una serie de etiquetas que exponen los componentes que implementan ciertas interfaces hacia el exterior, de modo que el cliente que usa el componente puede redefinir sus propiedades y comportamientos frente a eventos. En el siguiente ejemplo exponemos el componente de entrada de texto de nuestro componente por composición:

   <composite:interface displayName="Input text field with label, input and message components" >
   	...
	<composite:editableValueHolder name="input" />
	...
   </composite:interface>

De este modo, quien hace uso del componente puede asignar comportamientos típicos de la interfaz de implementa (conversores, validadores, eventos de acción y de cambio de valor):

  <tnt:inputText id="description" entity="#{beanTestView.item}" field="description">
    <f:validateLength for="input" minimum="1" maximum="20" />
  </tnt:inputText>

Para hacer esto en JSF 1.2 usábamos la etiqueta ui:insert y ahora podríamos hacer uso de la etiqueta composite:insertChildren, si bien sólo podríamos asignar el contenido de la etiqueta a un componente dentro del componente por composición, ahora es más parametrizable y permite crear componentes por composición más complejos. Además, si pensamos en cómo parametrizamos en JSF 1.2 el método del controlador que recibirá un evento de cambio de valor y lo trasladamos a JSF 2, rápidamente veremos los beneficios.

En general, se acabaron las etiquetas condicionales porque, al exponer los componentes hacía fuera es el cliente que los usa quien define su comportamiento.


5. Referencias.


6. Conclusiones.

Cada vez nos gusta más lo que vamos encontrando... ;-)

Se produce además la paradoja de que la mayoría de las librerías de componentes visuales de terceros con las que trabajamos, que son además en las que se ha inspirado la especificación (el Ajax de JSF 2, esrá inspirado en el de RichFaces: Ajax4JSF), por la complejidad de sus componentes, aún no soportan JSF 2, lo cual nos está dando pie a evaluar otras liberías más recientes que sí lo soportan: véase Primefaces.

Desde Autentia, continuaremos compartiendo el conocimiento que vamos adquiriendo y, si estáis interesados en el contenido de nuestros tutoriales y tenéis una necesidad formativa al respecto, no dudeis en poneros en contacto con nosotros. En Autentia nos dedicamos, además de a la consultoría, desarrollo y soporte a desarrollo, a impartir cursos de formación de las tecnologías con las que trabajamos.

Un saludo.

Jose

jmsanchez@autentia.com

A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL:

Fecha publicación: 2013-02-05-17:02:09

Autor: dvasquez

Hola, he seguido desarrollado el tutorial en una pequeña aplicación en NetBeans y GlassFis, y he tenido un problema con la ubicación de la carpeta 'template' en el WEB-INF. me sale un error del tipo 'java.io.FileNotFoundException', sin embargo si copio la carpeta template fuera del WEB-INF y modifico la ruta de la plantilla en la pagina xhmtl la aplicación funciona sin problema alguno. ¿podrían ayudarme o explicarme porque puede estar dando problemas poner los templates en el WEB-INF?, gracias.

Fecha publicación: 2011-07-04-08:37:43

Autor: jmsanchez

Buenas,

Muchas gracias por los comentarios.

La mayoría de los mismos me llegan también a través de correo electrónico y su respuesta con es lo suficientemente trivial como par añadirla aquí, dan contenido para otros tutoriales que iré publicando.

Stay tuned!

Un saludo.

Jose.

Fecha publicación: 2011-06-27-17:42:36

Autor: eric1986

Muy bueno el tutorial, pero tengo un problema:
en esta linea <p:menuitem label="#{msg.new}" url="#{menuBean.createItem}"></p:menuitem> del punto 3.2 no se me permite realizar esta sentencia ya que no encuentra en el contexto esa direccion, he probado con action y con actionlistener para usar el bean y al hacer click no me lleva a ninguna pagina, no hace nada.

Quisiera saber com hacerlo,

Gracias

Fecha publicación: 2011-06-27-17:39:08

Autor: eric1986

Muy bueno el tutorial, pero tengo un problema:
en esta linea <p:menuitem label="#{msg.new}" url="#{menuBean.createItem}"></p:menuitem> del punto 3.2 no se me permite realizar esta sentencia ya que no encuentra en el contexto esa direccion, he probado con action y con actionlistener para usar el bean y al hacer click no me lleva a ninguna pagina, no hace nada.

Quisiera saber com hacerlo,

Gracias

Fecha publicación: 2011-06-27-17:23:41

Autor: eric1986

Muy bueno el tutorial, pero tengo un problema:
en esta linea <p:menuitem label="#{msg.new}" url="#{menuBean.createItem}"></p:menuitem> del punto 3.2 no se me permite realizar esta sentencia ya que no encuentra en el contexto esa direccion, he probado con action y con actionlistener para usar el bean y al hacer click no me lleva a ninguna pagina, no hace nada.

Quisiera saber com hacerlo,

Gracias

Fecha publicación: 2011-05-23-16:25:44

Autor: chris456

Super tu tutorial Jose pero tengo una duda. Si el menu lo cargo con valores de la base de datos cada vez q llame a una pagina que usa el template seguramente volvera a cargar el menu nuevamente, que no es muy logico. ¿Como podria llamar a una pagina y que solo se actualize y se cargue en el tag
<div id="content"> ???. o de que manera podria plantear el problema ?

Fecha publicación: 2011-03-16-22:56:42

Autor: jmsanchez

Muchas gracias por los comentarios.

william, tenemos experiencia con ICEfaces en la versión community, pero no en la Enterprise. Aún así envianos un correo con el problema y, si podemos, te echaremos una mano.

Un saludo.

Jose.

Fecha publicación: 2011-03-16-22:48:10

Autor: ChristianPeru2731

Muchas gracias por el aporte, muy buen tutorial !!!!

Fecha publicación: 2010-12-10-17:35:05

Autor: icreativa

hola jose muy buena información, una consulta pues he adquirido el icefacesEE, pero no logro implementar la suite del icepack/composite-components no se hay algun documento q me pueda ayudar..
atte william gracias

Fecha publicación: 2010-11-22-00:59:35

Autor: jcarmonaloeches

Mola mola...

Fecha publicación: 2010-07-12-18:11:04

Autor: carlosgp

Muy bueno José.. sólo te faltó poner el código fuente para testear con el ejemplo.. pero muy bueno.