Lo ideal es usar los componentes estándar o usar librerías de componentes ya construidas, como Tomahawk (http://myfaces.apache.org/tomahawk/) o ICEfaces (http://www.icesoft.com/products/icefaces.html). Pero en algunas ocasiones no encontraremos ningún componente que cubra nuestras necesidades.
Para estas ocasiones siempre nos queda la opción de construirnos nuestros propios componentes. Esto es lo que vamos a ver en este tutorial.
En concreto vamos a construir un componente que, indicándole un POJO, nos pinte un formulario con una etiqueta y un campo de entrada para cada una de las propiedades de tipo String de ese POJO.
¡¡¡ Atención, el componente que vamos a construir sólo pretende ilustrar la construcción de un componente, pero todavía le quedará mucho para poder ser usando en producción !!!
Y ya, entrando en harina, podemos adelantar que para la construcción de un componente vamos a desarrollar tres piezas:
el componente: esta clase es realmente el componente, y es donde estará implementada la lógica asociada.
el render: es la clase que se encarga de pintar el componente y de recuperar la información introducida por el usuario para «inyectarla» en el componente. El render está asociado a un medio concreto (web, wap, …), mientras el componente es único. Es decir, podemos tener varios render para un mismo componente, o incluso reescribir los render si queremos cambiar el «como se pinta».
el tag: lo normal (aunque no obligatorio) es que nuestro componente lo queramos usar cómodamente en páginas JSP. El tag será la clase que implemente eso, una etiquetad de JSP.
En los siguientes apartados se ira viendo como construimos cada una de estas piezas.
Vamos a aprovechar y vamos a recordar el ciclo de vida de JSF. Para el desarrollo en JSF (bien sea de aplicaciones o de componentes) es fundamental comprender y dominar las 6 fases del ciclo de vida de JSF:
Restore view: Se reconstruye el árbol de componentes.
Apply request values: Se leen los valores de la request y se aplican sobre los componentes. En este momento es cuando se llama a los converters. Si hay algún error en la conversión se irá directamente a la fase «render response». Si un componente tiene «immediate=true» su validación (y el procesamiento de los eventos provocados por esa validación) se hará en esta fase .
Process validations: se validan todos los componentes (todos los que tienen «immediate=false», ya que los que lo tienen a true ya se validaron en la fase anterior). Si falla alguna de las validaciones se irá directamente a la fase «render response».
Update model values: Los valores de los back beans de JSF se actualizan con los valores que hasta ahora estaban guardados sólo en los componentes.
Invoke application: Se invoca a los métodos de los back beans.
Render response: se pinta la respuesta al usuario. Se pinta el árbol de componentes.
Aquí podéis encontrar el código completo de las tres clases necesarias para construir el componente.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
Hardware: Portátil Asus G1 (Core 2 Duo a 2.1 GHz, 2048 MB RAM, 120 GB HD).
Sistema Operativo: GNU / Linux, Debian (unstable), Kernel 2.6.21, KDE 3.5
Máquina Virtual Java: JDK 1.6.0-b105 de Sun Microsystems
Eclipse 3.2.2
MyFaces 1.1.5
Maven 2.0.7
3. El componente
A la hora de construir un componente debemos fijarnos si ya hay alguno que hace algo parecido, ya sea dentro del estándar, o de alguna librería que hayamos adquirido. Si ya tenemos un componente que hace algo parecido la mejor opción suele ser que nuestra clase extienda de ese componente para aprovechar las funcionalidades del padre. Si no encontramos ningún componente que podamos reutilizar tendremos que extender de la clase javax.faces.component.UIComponentBase.
En nuestro ejemplo vamos usar el segundo camino, extenderemos la clase UIComponentBase. Vamos a ir viendo paso a paso esta clase (recordar que en al final de la introducción tenéis acceso un tar.gz con el código completo):
public static final String COMPONENT_TYPE = FormBeanComponent.class.getName();
public static final String DEFAULT_RENDERER_TYPE = HtmlFormBeanRenderer.class.getName();
Estas dos constantes son cadena arbitrarias, aunque hemos decidido usar los nombres de las clases, se podría haber puesto cualquier otra cosa. Luego las utilizaremos para determinar el render que se tiene que usar para pintar el componente.
private ValueBinding beanBinding = null;
En este atributo guardaremos el bean a partir del cual vamos a pintar el formulario, y donde se guardaran los valores introducidos por el usuario (en la fase «update model values»). Es de tipo ValueBinding para poder usar lenguaje de expresiones de JSF para darle valor en la JSP.
private Map<String, String> localValues = new HashMap<String, String>();
Según hemos visto en el ciclo de vida, los valores de la request se almacenan en el componente hasta alcanzar la fase «update model values». Este atributo lo usaremos para guardar esos valores recuperados de la request. Ya hemos dicho que nuestro componente sólo trabajará con las propiedades de tipo String, así que en este mapa guardaremos como clave el nombre de la propiedad y como valor, el valor introducido por el usuario.
public FormBeanComponent() {
setRendererType(DEFAULT_RENDERER_TYPE);
}
Constructor de la clase. Es importante llamar a setRenderType(), este es un método de nuestro padre que permite definir cual será el identificador del render a usar. Si no hacemos esto luego JSF no será capaz de elegir el render apropiado para pintar el componente.
@Override
public String getFamily() {
return COMPONENT_TYPE;
}
Este es el único método que estamos obligados a implementar por extender la clase UIComponentBase. Este método devuelve el identificador de la familia a la que pertenece este componente. JSF, en función de este valor y del tipo de render (lo hemos definido en el constructor), elegirá el render apropiado para pintar este componente.
public Object getBean() {
final Object bean = beanBinding.getValue(FacesContext.getCurrentInstance());
return bean;
}
Este método devuelve el POJO que hemos asociado al componente (el POJO que usaremos para pintar el formulario y posteriormente para guardar los valores). Nótese como se extrae el valor del atributo beanBinding (dijimos que este atributo va a hacer referencia al POJO mediante lenguaje de expresiones de JSF). También hay «setter» correspondiente para fijar el valor del POJO (se puede ver en el código completo).
@Override
public void restoreState(FacesContext context, Object state) {
final Object values[] = (Object[])state;
super.restoreState(context, values[0]);
beanBinding = (ValueBinding)restoreAttachedState(FacesContext.getCurrentInstance(), values[1]);
}
@Override
public Object saveState(FacesContext context) {
List
2 COMENTARIOS
Saludos, Muy bueno el ejemplo,
Ahora tengo una duda siguiendo tu ejemplo cree mi propio componente, el cual funciona perfecto, pero se me presento un detalle, que a incluirlo en una pagina .xhmtl con facelets el componente no funciona correctamente, ya que por lo visto el facelest no ejecuta la clase manejadora del tag es decir la que hereda de (javax.faces.webapp.UIComponentTag) si no que trabaja directamente con el component y el renderer directamente, pero a no utilizar esta clase no ejecuta el metodo setProperties(UIComponent component); y este es el cual realiza el seteo de los atributos del componente, y hace que en el renderer de un error de NULL al no ser seteado los atributos, si tienes alguna sugerencia, seria de mucha hayuda y gracias de antemano
Tengo una consulta, como hago para integrar esto a paginas xhtml, segun otro articulo publicado aqui mismo (http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=migrateJSF2Facelets) , hice todos los pasos, pero no encuentro la forma que los componentes en el \\\»xhtml\\\» me desplieque las propuedades que necesito asignarles, solo me despliequa el id y yo tengo varias propiedades en el tld (name, values, label,requerida, etc.) y programadas en una clase \\\»InputEntryTag\\\» pero esto no lo puedo habilitar ejmp de como se me muestra acutalmenete en le xhtml: \\\»\\\»
Saludos, Muy bueno el ejemplo,
Ahora tengo una duda siguiendo tu ejemplo cree mi propio componente, el cual funciona perfecto, pero se me presento un detalle, que a incluirlo en una pagina .xhmtl con facelets el componente no funciona correctamente, ya que por lo visto el facelest no ejecuta la clase manejadora del tag es decir la que hereda de (javax.faces.webapp.UIComponentTag) si no que trabaja directamente con el component y el renderer directamente, pero a no utilizar esta clase no ejecuta el metodo setProperties(UIComponent component); y este es el cual realiza el seteo de los atributos del componente, y hace que en el renderer de un error de NULL al no ser seteado los atributos, si tienes alguna sugerencia, seria de mucha hayuda y gracias de antemano
Tengo una consulta, como hago para integrar esto a paginas xhtml, segun otro articulo publicado aqui mismo (http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=migrateJSF2Facelets) , hice todos los pasos, pero no encuentro la forma que los componentes en el \\\»xhtml\\\» me desplieque las propuedades que necesito asignarles, solo me despliequa el id y yo tengo varias propiedades en el tld (name, values, label,requerida, etc.) y programadas en una clase \\\»InputEntryTag\\\» pero esto no lo puedo habilitar ejmp de como se me muestra acutalmenete en le xhtml: \\\»\\\»