Tabla de Contenidos

Primefaces

PEDRO DELGADO YARZA 2014/01/29 13:48

Versión: 4.0.7

En esta sección vamos a exponer los componentes primefaces recomendados desde MNCS para desarrollar las aplicaciones Fundeweb 2.0. En caso de querer consultar el listado completo de los componentes primefaces disponibles, podéis visitar su página de demostración en la url http://www.primefaces.org/showcase/ui/home.jsf

Es importante destacar que la versión de primefaces que contiene esa página de demo no está disponible aún para los desarrolladores, por lo que algún componente puede comportarse de manera ligeramente diferente a la vista en la demo. Los componentes sujetos a cambios en la versión están sombreados en el menú de demostración de primefaces.

Los componentes básicos o con una explicación suficiente en la demo de primefaces aparecen únicamente referenciados en esta guía. Los que hemos encontrado algo más complejos, novedosos, o que requieren un comportamiento especial han sido analizados. Estos análisis se pueden ampliar en base a las necesidades que surjan durante los desarrollos.

Componentes de entrada de datos

AutoComplete

http://www.primefaces.org/showcase/ui/autocompleteHome.jsf

Este componente es una variante del componente InputText que filtra en el modelo los valores que concuerden con los caracteres introducidos en el filtro. Se puede permitir que se introduzcan valores no existentes en el filtro o impedirlo (según se desee).

Las propiedades a destacar son las siguientes:

  1. minQueryLength: Indica el número mínimo de caracteres que deberemos escribir para mostrar la lista de opciones.
  2. completeMethod: Método que devuelve la lista de opciones en base a los caracteres introducidos.
  3. converter: Conversor para los objectos que maneje el componente.
  4. forceSelection: Fuerza que el valor introducido en el input deba estar dentro de la lista de opciones o no.

Un ejemplo del código html

   <p:autoComplete value="#{manejadorComponentesInput.provinciaSeleccionada}"
		id="autocompletePojo" completeMethod="#{manejadorComponentesInput.autocompleteProvincia}"
		var="provincia" itemLabel="#{provincia.proNombre}"
		itemValue="#{provincia}" converter="conversorProvincias"
		forceSelection="true" />

En este caso hemos mapeado directamente los datos a POJOs por lo que el código del conversor quedaría de la siguiente manera:

@FacesConverter(value = "conversorProvincias", forClass = Provincias.class)
@RequestScoped
public class ConversorProvincias implements Converter {
	private static final Logger log = Logger
			.getLogger(ConversorProvincias.class);
 
        //BeanManager necesario para recuperar los beans que hay cargados en el servidor
        //es el equivalente el Component.getInstance
	private BeanManager bm;
	@Override
	public Object getAsObject(FacesContext context, UIComponent component,
			String value) {
		ResourcesUtil ru = null;
		try {
			bm = (BeanManager) InitialContext.doLookup("java:comp/BeanManager");
			//Recuperamos un bean de utilidad que contiene al entityManager que necesitamos
                        //para realizar la búsqueda
                        for (Bean b : bm.getBeans(ResourcesUtil.class)) {
				ru = (ResourcesUtil) bm.getReference(b, ResourcesUtil.class,
						bm.createCreationalContext(b));
			}
 
		} catch (NamingException e) {
			log.error("Error obteniendo entityManager", e);
		}
		if (!UtilString.esCadenaVacia(value)) {
			Query consulta = ru.getEm().createNamedQuery("obtenerProvinciasId");
			consulta.setParameter("codigo", value);
			return consulta.getSingleResult();
		} else {
			return null;
		}
	}
 
	@Override
	public String getAsString(FacesContext context, UIComponent component,
			Object value) {
		if (value != null) {
			return ((Provincias) value).getProCodigo();
		} else {
			return "";
		}
	}
 
}

Por último el código del método que devuelve la lista de opciones en base a los datos introducidos:

     public List<Provincias> autocompleteProvincia(String query) {
	List<Provincias> provincias = new ArrayList<Provincias>();
	Query consulta = em.createNamedQuery("obtenerProvinciasLike");
	consulta.setParameter("filtro",query.toUpperCase()+"%");
	provincias = consulta.getResultList();
	return provincias;
     }

BoolCheckbox

http://www.primefaces.org/showcase/ui/selectBooleanCheckbox.jsf

Calendar

http://www.primefaces.org/showcase/ui/calendarAjax.jsf

Componente simple de selección de fecha, que o bien puede mostrarse al hacer click sobre el campo de texto asociado, o bien mediante un botón. Dentro de las características principales podemos destacar las siguientes:

  1. pattern: Permite especificar el patron de fecha que queremos mostrar.
  2. shwOn: Permite especificar si queremos que el calendario se despliegue al pulsar un botón.
  3. locale: Permite especificar el idioma en el que se mostrará el calendario.

Importante: Fundeweb ya incluye las librerias necesarias para el locale en castellano, no obstante si se quiere añadir otro idioma diferente del inglés o el español, o bien para aplicaciones no Fundeweb, es necesario, aparte de especificar el locale al componente, añadir en el directorio de recursos un fichero de nombre “calendar_(nombre del locale).js” (calendar_es.js para español). Un ejemplo del contenido de ese fichero (en español) es el siguiente:

PrimeFaces.locales['es'] = {
    closeText: 'Cerrar',
    prevText: 'Anterior',
    nextText: 'Siguiente',
    monthNames: ['Enero','Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    monthNamesShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun','Jul','Ago','Sep','Oct','Nov','Dic'],
    dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'],
    dayNamesShort: ['Dom','Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab'],
    dayNamesMin: ['D','L','M','X','J','V','S'],
    weekHeader: 'Semana',
    firstDay: 1,
    isRTL: false,
    showMonthAfterYear: false,
    yearSuffix: '',
    timeOnlyTitle: 'Sólo hora',
    timeText: 'Tiempo',
    hourText: 'Hora',
    minuteText: 'Minuto',
    secondText: 'Segundo',
    currentText: 'Fecha actual',
    ampm: false,
    month: 'Mes',
    week: 'Semana',
    day: 'Día',
    allDayText : 'Todo el día'
}

Un ejemplo del código del componente sería el siguiente:

	<p:calendar id="fechaAnuncio" pattern="dd/MM/yyyy" 
                    value="#{manejadorAnuncios.anuncioSeleccionado.fechaPublicacion}"
                    locale="es" />

Editor

http://www.primefaces.org/showcase/ui/editor.jsf

Editor de texto enriquecido con barra de opciones configurable mediante la propiedad “controls”. El contenido del editor se guarda en formato html utilizando los caracteres de escape para los signos reservados. Las posibles opciones de configuración de la propiedad “controls” se especifican una a una separas por espacios.

Estas opciones son las siguientes: bold, italic, underline, strikethrough, subscript, superscript, font, size, style, color, highlight, removeformat, bullets, numbering, outdent, indent alignleft, center, alignright, justify, undo, redo, rule, image, link, unlink, cut, copy, paste, pastetext, print, source (muestra el codigo html que se va generando)

Un ejemplo de código sería el siguiente:

    <p:editor id="editAnuncio" width="600" controls="bold italic underline strikethrough size font bullets numbering"
		rendered="#{manejadorAnuncios.modoEdicion}"
		value="#{manejadorAnuncios.anuncioSeleccionado.descripcion}"
		readonly="#{manejadorAnuncios.modoEdicion}" />

FileUpload

http://www.primefaces.org/showcase/ui/fileUploadMultiple.jsf

Componente de subida de ficheros. Permite subir múltiples ficheros y limitar tanto el tamaño y tipo de los mismos, como la cantidad máxima de ficheros a subir. Podemos hacer uso de dos casos, el simple que utiliza características del navegador y presenta una funcionalidad más recortada, o el avanzado que amplia las funcionalidades del componente, modifica el estilo visual y está basado en HTML5 (importante asegurar la compatibilidad del navegador).

Modo simple Este modo es parecido al que se puede simular por javascript. Recoge un fichero simple y lo sube al servidor tras hacer submit. Requiere, por tanto, un botón para ejecutar la acción que suba el documento.

    <p:fileUpload value="#{manejadorFicheros.fichero}" mode="simple" />
    <p:commandButton value="Submit" ajax="false" actionListener="#{manejadorFicheros.agregaFichero()}" />

Modo avanzado En este modo se nos permiten funcionalidades avanzadas como subir múltiples documentos a la vez usando ajax. Validaciones directas en el componente, visualización del listado de documentos, etc…

Las propiedades más relevantes son:

  1. multiple: Permite la subida múltiple de ficheros.
  2. sizeLimit: Tamaño máximo de fichero en bytes.
  3. auto: Subida automática de ficheros.
  4. allowTypes: Expresión regular para filtrar el tipo de ficheros.

El componente por defecto limpia la lista de los ficheros que se han subido, en caso de querer mantenerla, de momento, se debe hacer mediante un componente a parte que se refresque con los nombres de los mismos.

El código de ejemplo es el siguiente:

     <p:fileUpload
	fileUploadListener="#{manejadorComponentesInput.subidaFichero}"
	mode="advanced" dragDropSupport="true" multiple="true" 
	update=":messages" sizeLimit="10000000" fileLimit="3"
	invalidFileMessage="#{messages['fileupload.invalid.type']}"
	invalidSizeMessage="#{messages['fileupload.invalid.size']}"
	fileLimitMessage="#{messages['fileupload.invalid.fileNumber']}"
	cancelLabel="#{messages['value.cancel']}"
	label="#{messages['fileupload.label.upload']}"
	auto="true" allowTypes="/(\.|\/)(pdf|gif|jpeg|png)$/" />

InputMask

http://www.primefaces.org/showcase/ui/inputMask.jsf

Este componente nos permite asignar un patrón a un inputText de manera que los datos que introduzcamos se ajusten al patrón especificado.

Para completar la funcionalidad podemos añadir un validador de patrones para lanzar los posibles errores que pudiera ocasionar la inserción de los datos. La propiedad pattern nos permite indicar el patrón que tendrá el valor a introducir. Para ver más ejemplos de patrones, visitad el enlace del componente.

El código de ejemplo es el siguiente:

        <div style="float: left;">
		<p:outputLabel for="mask" value="Fecha manual "
			style="margin-right:10px;" styleClass="nombreLabel" />
		<p:inputMask id="mask" value="#{manejadorComponentesInput.fecha}"
			mask="99/99/9999" required="true"
			validator="#{manejadorComponentesInput.validaFecha}"
			validatorMessage="#{messages['form.fecha.invalida']}">
	                <!-- Agregamos un validador para el patron de fecha -->
         		<f:validateRegex pattern="[0-9]{2}/[0-1][0-9]/[0-9]{4}"	for="mask" />
                        <!-- Rerenderizamos los mensajes de error cuando el componente pierde el foco -->
         		<p:ajax update="iconoErrorFecha" event="blur" />
		</p:inputMask>
	</div>
	<div style="float: left">
                <!-- Muestra un icono de error junto al inputText -->
	        <p:message for="mask" id="iconoErrorFecha" display="icon" showDetail="false" />
	</div>

InputText

http://www.primefaces.org/showcase/ui/inputText.jsf

InputTextarea

http://www.primefaces.org/showcase/ui/inputTextarea.jsf

OneListbox

http://www.primefaces.org/showcase/ui/selectOneListbox.jsf

Password

http://www.primefaces.org/showcase/ui/password.jsf

PickList

http://www.primefaces.org/showcase/ui/picklist.jsf

Este componente nos permite seleccionar un conjunto de elementos en una lista. Primefaces incorpora una clase Java DualListModel que permite especificar la lista origen y la lista destino. http://www.primefaces.org/docs/api/3.4/org/primefaces/model/DualListModel.html

Nota: Para ocultar los botontes añadir o eliminar todos hay que sobreescribir el css del botón:

.ui-picklist-button-add-all {
   visibility: hidden !important;
}
 
.ui-picklist-button-remove-all {
   visibility: hidden !important;
}

El código del componente

     <p:pickList id="provinciasPic"
	value="#{manejadorComponentesInput.provinciasPick}" var="provincia" itemValue="#{provincia}"
	itemLabel="#{provincia.proNombre}" converter="conversorProvincias">
	<f:facet name="sourceCaption">Disponibles</f:facet>
	<f:facet name="targetCaption">Seleccionadas</f:facet>
	<p:ajax event="transfer" listener="#{manejadorComponentesInput.onTransfer}" />
     </p:pickList>

Rating

http://www.primefaces.org/showcase/ui/rating.jsf

Schedule

Este componente nos provee de una agenda al estilo Outlook para anotar diferentes eventos. Permite crearlos, modificarlos y moverlos. Este componente debe estar controlado por un bean de tipo sesión.

Las propiedades más importantes son:

  1. timeZone: Para ajustar el timezone a nuestra franja horaria.
  2. slotMinutes: Nos permite definir el rango de minutos que contiene un slot en las vistas de semana o día.
  3. view: 'month', 'agendaDay', 'agendaWeek' nos permiten seleccionar la vista por defecto del componente.
  4. allDaySlot: Indica si en la vista de día o semana se añade una ranura para indicar “todo el día”.

El componente tiene una serie de eventos que nos sirven para interactuar con él. Los más destacados son:

  1. dateSelect: Evento que se lanza cuando hacemos click sobre una fecha de la agenda.
  2. eventSelect: Evento que se lanza cuando hacemos click en un evento de la agenda.
  3. eventMove: Evento que se lanza cuando movemos un evento de la agenda.
  4. evenResize: Evento que se lanza cuando redimensionamos un evento de la agenda .

El código fuente es el siguiente:

	<p:schedule id="agenda" value="#{agendaBean.agenda}"
		widgetVar="agendaWV" timeZone="GMT+1" allDaySlot="false" > 
		<p:ajax event="dateSelect" listener="#{agendaBean.onDateSelect}"
			update="eventDetails" oncomplete="dialogoEvento.show()" />
		<p:ajax event="eventSelect" listener="#{agendaBean.onEventSelect}"
			update="eventDetails" oncomplete="dialogoEvento.show()" />
		<p:ajax event="eventMove" listener="#{agendaBean.onEventMove}"
			update=":messages" />
		<p:ajax event="eventResize" listener="#{agendaBean.onEventResize}"
			update=":messages" />
	</p:schedule>
	<p:dialog widgetVar="dialogoEvento" header="Event Details"showEffect="clip" hideEffect="clip">
		<h:panelGrid id="eventDetails" columns="2">
			<h:outputLabel for="title" value="#{messages['agenda.evento.nombre']}" />
			<p:inputText id="title"	value="#{agendaBean.eventoSeleccionado.title}" required="true" />
 
			<h:outputLabel for="from" value="#{messages['agenda.evento.desde']}" />
			<p:inputMask id="from" value="#{agendaBean.eventoSeleccionado.startDate}" mask="99/99/9999">
			<f:convertDateTime pattern="dd/MM/yyyy" timeZone="GMT+1" locale="es_ES" />
			</p:inputMask>
 
			<h:outputLabel for="to" value="#{messages['agenda.evento.hasta']}" />
			<p:inputMask id="to" value="#{agendaBean.eventoSeleccionado.endDate}" mask="99/99/9999">
			<f:convertDateTime pattern="dd/MM/yyyy" timeZone="GMT+1" locale="es_ES" />
         		</p:inputMask>
 
			<p:commandButton id="addButton" value="Save"
				actionListener="#{agendaBean.guaradarEvento}" update="agenda"
				oncomplete="dialogoEvento.hide();" />
			<p:commandButton type="reset" value="Reset"
				oncomplete="dialogoEvento.hide();" />
		</h:panelGrid>
	</p:dialog>

SelectOneMenu

http://www.primefaces.org/showcase/ui/selectOneMenu.jsf

Este componente, está representado por un ComboBox que nos permite seleccionar un valor. En este ejemplo hemos ilustrado este componente mostrando los resultados ordenados por grupos.

El codigo html de ejemplo es:

    <p:selectOneMenu id="selectOneMenu"
 		value="#{manejadorComponentesInput.provinciaSeleccionada}">
		<f:selectItem itemLabel="Elija provincia" itemValue="" />
		<f:selectItems value="#{manejadorComponentesInput.provinciasAlfabeticas}" />
   </p:selectOneMenu>

Para poder agrupar los resultados deberemos crear SelectItems e introducirlos en SelectItemGroup de la siguiente manera:

        //Key es el label que queremos que se muestre en el grupo
	SelectItemGroup grupo = new SelectItemGroup(key);
        //setSelectItems requiere un array, para ello transformamos a array una lista de objetos de tipo SelectItems
        //Para ello hacemos uso de la clase Arrays
        grupo.setSelectItems(Arrays.copyOf(provs.get(key).toArray(), 
              provs.get(key).toArray().length, SelectItem[].class));
 

SelectOneRadio

http://www.primefaces.org/showcase/ui/selectOneRadio.jsf

Este componente crea un grupo de radiobuttons que nos permite seleccionar una opción de entre las expuestas. Al mismo tiempo nos permite diseñar el layout de los botones como queramos dándonos total libertad y permitiéndonos combinarlo con otro tipo de componentes.

Para poder crear un diseño personalizado tenemos que poner la opcion layout con valor custom. Para posteriormente codificar la visualización del mismo.

En el ejemplo que mostraremos a continuación hemos diseñado un radiobutton vertical con un componente adicional en cada opción.

    <p:outputPanel id="panelRadio">
	<p:selectOneRadio id="radiobut"
        	value="#{manejadorComponentesInput.radioOption}" layout="custom">
 	  <f:selectItem itemLabel="Opcion 1" itemValue="1" />
	  <f:selectItem itemLabel="Opcion 2" itemValue="2" />
	  <f:selectItem itemLabel="Opcion 3" itemValue="3" />
	  <p:ajax update="panelRadio" />
	</p:selectOneRadio>
	<h:outputText value="#{manejadorComponentesInput.radioOption}" />
 
	<h:panelGrid columns="3" cellspacing="10px;" id="panelCustomRadio">
	   <p:outputLabel for="opcion1" value="Valor" styleClass="nombreLabel" style="font-size:0.7em;" />
	   <p:radioButton id="opcion1" for="radiobut" itemIndex="0" />
	   <p:spinner style="margin-left:20px;"	disabled="#{manejadorComponentesInput.radioOption!=1}" />
 
	   <p:outputLabel for="opcion2" value="Valoración" styleClass="nombreLabel" style="font-size:0.7em;" />
	   <p:radioButton id="opcion2" for="radiobut" itemIndex="1" />
	   <p:rating style="margin-left:20px;" disabled="#{manejadorComponentesInput.radioOption!=2}" />
 
	   <p:outputLabel for="opcion3" value="Teléfono" styleClass="nombreLabel" style="font-size:0.7em;" />
	   <p:radioButton id="opcion3" for="radiobut" itemIndex="2" />
	   <p:inputMask id="maskTlf" style="margin-left:20px;" value="#{manejadorComponentesInput.fecha}"
			mask="+99(999)-999999" required="true"	validator="#{manejadorComponentesInput.validaFecha}"
			disabled="#{manejadorComponentesInput.radioOption!=3}">	</p:inputMask>
	</h:panelGrid>
	</p:outputPanel>

Importante: Actualmente hay un bug en chrome e IE que al refrescar con ajax el componente se queda deshabilitado, funciona correctamente en Firefox.

Spinner

http://www.primefaces.org/showcase/ui/spinner.jsf

Tree

http://www.primefaces.org/showcase/ui/treeHome.jsf

Cuando queremos incorporar un menú contextual en un árbol, si seguimos las instrucciones indicadas en el showcase de primefaces, nos encontramos ante el problema de que cada vez que queremos mostrar el menú se realiza un submit lo que causa pequeñas detenciones cada vez que queramos mostrarlo.

Estas detenciones resultan incómodas si queremos mostrar con relativa frecuencia el menú contextual.

Para solventar este problema se ha diseñado una solución que permite cargar el menú contextual inmediatamente retrasando el submit a la selección de una acción en dicho menú. En este caso es necesario diseñar un menú contextual diferente según los casos que tengamos.

El código fuente quedaría así:

<p:tree id="tTitulaciones" value="#{solicitudOfertaEnsenanzaBean.root}" var="titulacion"
   style="font-family:arial; font-size: x-small;" cache="true"  dynamic="true" highlight="true"
   selectionMode="single"   selection="#{solicitudOfertaEnsenanzaBean.selectedNode}" animate="true">                                                
 
       <p:treeNode type="padre">
            <p:graphicImage value="/resources/img/#{titulacion.semaforo}.png"  styleClass="ui-semaforo" />
            <h:outputText value="#{titulacion.etiqueta}" />
       </p:treeNode>
 
       <p:treeNode type="hijo">
             <p:graphicImage  value="/resources/img/#{titulacion.semaforo}.png" styleClass="ui-semaforo" />
             <h:outputText value="#{titulacion.etiqueta}" />
        </p:treeNode>
 
</p:tree>

En esta definición del árbol indicamos un tipo para cada nodo diferente, cada tipo tendrá un menú contextual diferente asociado, de tal manera que, internamente por javascript, se dibujará el componente concreto sin necesidad de hacer el submit.

 <s:div id="menuContextual" >
        <p:contextMenu for="tTitulaciones" widgetVar="menuContextual1" nodeType="padre"
           style="font-family:arial; font-size: x-small; width:180px" >
           <p:menuitem value="Menu 1" update="titulacionPanel" icon="ui-icon-info" oncomplete="PF('titulacionDialog').show()" />
         </p:contextMenu>
 
        <p:contextMenu for="tTitulaciones" widgetVar="menuContextual2" nodeType="hijo"
           style="font-family:arial; font-size: x-small; width:180px" >
           <p:menuitem value="Menu 2" update="titulacionPanel" icon="ui-icon-info" oncomplete="PF('titulacionDialog').show()" />
            <p:menuitem value="Menu 3" update="titulacionPanel" icon="ui-icon-info" oncomplete="PF('titulacionDialog').show()" />
        </p:contextMenu>
</s:div>

Por último en el bean de respaldo debemos asegurar que los nodos que se crean contienen los tipos tratados en el código xhtml

 for(VwTitulacionesXCentro tituCentro : listaTitulaciones){
            tituCentro.setTipoNodo("padre");
            //Insertar en arbol           
            TreeNode padre = new DefaultTreeNode(tituCentro.getTipoNodo(),tituCentro, root);
            //Si tiene hijos, insertar los hijos
            listaDepartamentosTitu = this.cargarDepartamentosXTitu(tituCentro.getId().getCodTitu() );           
               for(VwDepartamentosXTitu hijo: listaDepartamentosTitu){
                    hijo.setTipoNodo("hijo");                   
                    new DefaultTreeNode(hijo.getTipoNodo(),hijo, padre);
                }           
}

TreeTable

http://www.primefaces.org/showcase/ui/treeTableHome.jsf

Este componente nos permite combinar la funcionalidad de árbol con la claridad de agrupación de una tabla. La principal diferencia con un árbol es que todos los objetos deben devolver algún valor para cada una de las columnas, por lo que hay que tener en cuenta esto a la hora de mostrar los datos.

Este componente permite que se le añada un menú contextual para realizar acciones avanzadas. Este menú se mostrará al hacer clic derecho sobre el nodo correspondiente.

El código de ejemplo es el siguiente

    <p:treeTable id="treetable" style="width:70%;" emptyMessage="No hay datos" selectionMode="single"
	value="#{manejadorComponentesInput.root}" var="prov">
	<f:facet name="header"> Selector de provincias </f:facet>
	<p:column style="width:12%">
		<f:facet name="header"> Provincia  </f:facet>
		<h:outputText value="#{prov.proNombre}" />
	</p:column>
	<p:column style="width:12%">
		<f:facet name="header">  Longitud Nombre  </f:facet>
		<h:outputText value="#{!prov.proCodigo.equals('no') ? prov.proNombre.length() : '--'}" />
	</p:column>
	<p:column style="width:4%">
		<f:facet name="header">  Acciones  </f:facet>
		<p:commandLink styleClass="ui-icon ui-icon-search" />
	</p:column>
     </p:treeTable>

CheckBox TreeTable

Esta es una variedad de el TreeTable normal que añade un checkbox a cada fila para realizar acciones determinadas sobre los elemento seleccionados. Su encabezado sería:

<p:treeTable value="#{ofertaAction.oferta5}" var="asi"
	selectionMode="checkbox" selection="#{ofertaAction.listadoOrigenOferta5}"
	id="panelAsiPrincipal">
        ...
        ...
</p:treeTable>

Las propiedades a destacar:

Bugs: Actualmente el árbol presenta un bug al borrar los elementos ya que entra en un bucle infinito al intentar hacer un “remove(Nodo)”.

Incidencia: Bucle infinito al borrar un nodo

La solución a este bug es utilizar la siguiente clase:

package automatricula.oferta.backbeans;
 
import java.util.ArrayList;
 
import org.primefaces.model.CheckboxTreeNode;
import org.primefaces.model.TreeNode;
 
public class FundewebTreeNode extends CheckboxTreeNode{
 
	private static final long serialVersionUID = -5055108978844454329L;
	private TreeNode parent;
 
 
	public FundewebTreeNode(Object data, TreeNode parent) {
		super(data,parent);		
	}
 
	@Override
	public void setParent(TreeNode parent) {
                this.parent = parent;
	}
 
	public TreeNode getParent() {
		return parent;
	}
 
 
 
}

Como vemos esta clase extiende a CheckboxTreeNode y sobreescribe el método setParent que es el causante del error. Una vez creada esta clase cuando creemos nuestro árbol los nodos se deberán crear de este tipo.

	TreeNode nodo = new FundewebTreeNode(...);

Wizard

http://www.primefaces.org/showcase/ui/wizard.jsf

Este componente permite de manera sencilla crear un wizard para la inserción de datos en el sistema permitiéndonos navegación hacia delante y hacia atrás. Este comportamiento no incluye un botón “Finalizar” en la última pantalla por defecto, por lo que deberemos añadirlo nosotros mismos en el último formulario, o bien hacer un estilo de botones personalizado.

Las propiedades más relevantes del componente son las siguientes:

  1. flowListener: Método java que recibe los eventos de cambio de pestaña (avanzamos o retrocedemos en el menú).
  2. showNavBar: Nos permite quitar los botones, por defecto, que trae el Wizard para añadir los nuestros.

Un ejemplo de código

    <p:wizard id="wizard" flowListener="#{wizardBean.cambioMenu}"
	widgetVar="wiz" backLabel="#{messages['pagination.before']}"
	nextLabel="#{messages['pagination.next']}">
	<p:tab id="personal" title="#{messages['wizard.datos.pers']}">
		<p:panel>
			<h:panelGrid columns="3" cellspacing="20px;">
			<h:panelGroup>
				<p:outputLabel value="#{messages['madet.nombre.head']}" styleClass="nombreLabel" for="nombre" />
 
				<p:inputText id="nombre" style="margin-left:20px;" required="true" />
			</h:panelGroup>
			<h:panelGroup>
				<p:outputLabel value="#{messages['wizard.apellid']}" styleClass="nombreLabel" for="apellidos" />
				<p:inputText id="apellidos" style="margin-left:20px;" required="true" />
			</h:panelGroup>
			<h:panelGroup>
				<p:outputLabel value="#{messages['wizard.nacimiento']}"
					styleClass="nombreLabel" for="autocompletePojo" />
				<p:autoComplete style="margin-left:20px;"
					value="#{manejadorComponentesInput.provinciaSeleccionada}" id="autocompletePojo"
					completeMethod="#{manejadorComponentesInput.autocompleteProvincia}"
					var="provincia" itemLabel="#{provincia.proNombre}"
					itemValue="#{provincia}" converter="conversorProvincias"
					forceSelection="true" required="true" />
			</h:panelGroup>
			</h:panelGrid>
		</p:panel>
	</p:tab>
	<p:tab id="estadisticos" title="#{messages['wizard.datos.est']}">
		<p:panel>
			<h3>Panel datos estadísticos</h3>
		</p:panel>
	</p:tab>
	<p:tab id="asignaturas" title="#{messages['wizard.datos.asig']}">
		<p:panel>
			<h3>Selección de asignaturas</h3>
		</p:panel>
	</p:tab>
	<p:tab id="pago" title="#{messages['wizard.datos.pago']}">
		<p:panel>
			<h3>Detalles del pago</h3>
		</p:panel>
	</p:tab>
	<p:tab id="fin" title="#{messages['wizard.datos.fin']}">
		<p:panel>
                       <h3>Panel resumen final</h3>
		</p:panel>
	</p:tab>
</p:wizard>

En el caso que queramos personalizar los botones del wizard debemos ocultar la barra de navegacion showNavBar:false e incorporar después del wizard los botones correspondientes.

Para poder navegar hacia delante o hacia atrás debemos invocar la acción javascript correspondiente del wizard, para ello lo invocaremos usando el valor de la propiedad widgetVar=“wiz” de la siguiente manera:

	<p:commandButton action="#{wizardBean.accion()}" id="anterior"
	        oncomplete="wiz.back()" value="#{messages['pagination.before']}" 
		style="float:left;"/>
 
	<p:commandButton action="#{wizardBean.accion()}" id="siguiente"   oncomplete="wiz.next()" style="float:right;"
		value="Siguiente"/>

Componentes de salida de datos

- http://www.primefaces.org/showcase/ui/carousel.jsf

Chart

http://www.primefaces.org/showcase/ui/barChart.jsf

Este componente permite dibujar diferentes diagramas para mostrar datos. En este caso hemos escogido el diagrama de barras que tiene las siguientes propiedades de interés:

  1. legendPosition: Posición de la leyenda dentro del diagrama.
  2. legendCols: Número de columnas de la leyenda (permite mostrarla en vertical u horizontal).
  3. shadow: Sobrea las columnas.
  4. animate: Muestra el diagrama mediante una animación.
  5. showDatatip: Muestra el valor de la columna al pasar el ratón por encima.
  6. datatipFormat: Establece el formato del DataTipo que se muestra al pasar por la columna.

El código fuente para el diagrama es el siguiente:

     <p:barChart id="basico"
	value="#{manejadorComponentesInput.chartBarras}"
	legendPosition="nw" title="#{messages['diagrama.barras.titulo']}"
	min="0" max="210" style="height:400px" legendCols="1"
	shadow="true" animate="true" showDatatip="true"
	datatipFormat="#{manejadorComponentesInput.formatoBarra}" />

DataExporter

http://www.primefaces.org/showcase/ui/exporter.jsf

Este componente nos permite exportar en diferentes formatos(PDF,XML,XSL,CSV) el contenido de una tabla. Permitiéndonos exportar toda la tabla o sólo la página que estamos viendo. Importante: Para la exportación de PDF hay que asegurarse de que tenemos la librería de iText configurada en nuestro proyecto.

Las propiedades más importantes son:

En la tabla

  1. exportable: Situado en el componente “<p:column>” nos indica si queremos que esa columna sea exportable o no.
  2. <f:facet header> Deberemos nombrar así la cabecera de la columna para que aparezca en la exportación.

En el componente

  1. type: El tipo de exportacion que queremos realizar (pdf,xls,xml,csv).
  2. target: El id de la tabla que queremos exportar.

El código de ejemplo es el siguiente:

	<h:commandLink style="float:right;">
		<p:graphicImage value="/resources/img/xml.png" />
		<p:dataExporter type="xml" target="listaAnuncios" fileName="listado" pageOnly="true" />
	</h:commandLink>
 
        <p:dataTable id="listaAnuncios" var="anuncio"
		value="#{manejadorAnuncios.buscadorAnuncios}" paginator="true"
		rows="10" paginatorPosition="bottom"
		paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
		rowsPerPageTemplate="5,10,15,30"
		selection="#{manejadorAnuncios.anuncioSeleccionado}" lazy="true"
		emptyMessage="#{messages['datatable.sinresultados']}">
		<p:column sortBy="#{anuncio.id}">
			<f:facet name="header">
				<h:outputText value="#{messages['madet.num.head']}" />
			</f:facet>
			<h:outputText value="#{anuncio.id}" />
		</p:column>
         </p:dataTable>

DataList

http://www.primefaces.org/showcase/ui/dataListUnordered.jsf

DataGrid

http://www.primefaces.org/showcase/ui/datagrid.jsf

DataTable

http://www.primefaces.org/showcase/ui/datatablePagination.jsf

Tabla que muestra los elementos almacenados en el bean de respaldo en forma de listado. Primefaces nos proporciona diversos tipos de tablas, siendo la más recomendable la tabla paginada con carga difertida (LazyLoad DataTable) que es la que tratamos en este ejemplo.

Dicha tabla nos provee de filtrado, ordenamiento y paginación manteniendo en memoria sólo la información que se está mostrando. Para ello necesitamos crear una estructura de clases concreta para que el componente realice las cargas conforme vayamos paginando.

Las principales propiedades de este componente son las siguientes:

  1. paginator: Indica si queremos paginación.
  2. rows: Número de líneas que se muestran por defecto.
  3. paginatorPosition: Posición donde queremos que se muestre la paginación.
  4. paginatorTemplate: Permite indicar qué botones queremos mostrar en el paginador.
  5. rowsPerPageTemplate: Permite indicar los diferentes números de líneas que podemos mostrar en la pantalla para que el usuario seleccione cuales quiere visualizar.
  6. lazy: Permite indicar si se cargan todos los datos de golpe o no.
  7. emptyMessage: Permite indicar el mensaje que mostramos cuando no hay filas disponibles.

El código html de ejemplo de esta tabla es el siguiente:

<p:dataTable id="listaAnuncios" var="anuncio"
		value="#{manejadorAnuncios.buscadorAnuncios}" paginator="true"
		rows="10" paginatorPosition="bottom"
		paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
		rowsPerPageTemplate="5,10,15,30"
		selection="#{manejadorAnuncios.anuncioSeleccionado}" lazy="true"
		emptyMessage="#{messages['datatable.sinresultados']}">

Hay que destacar que manejadorAnuncios.buscadorAnuncios, devuelve una clase que extiende la clase abstracta LazyDataModel. Esta clase nos provee del mecanismo de carga bajo demanda que necesitamos para nuestra tabla.

El código de esta clase para el ejemplo es el siguiente:

public class LazyAnuncioDataModel extends LazyDataModel<Anuncio> {
 
	private static final long serialVersionUID = -7128353552033320971L;
 
	private ServicioAnuncios servicioAnuncios;
 
	private String namedQuery;
	private String countQuery;
	private HashMap<String, Object> parametros;
	private List<Anuncio> anuncios;
 
	public LazyAnuncioDataModel(ServicioAnuncios servicioAnuncios) {
		this.servicioAnuncios = servicioAnuncios;
		anuncios = new ArrayList<Anuncio>();
	}
 
	@Override
	public Object getRowKey(Anuncio anuncio) {
		return anuncio.getId();
	}
 
	@Override
	public List<Anuncio> load(int first, int pageSize, String sortField,
			SortOrder sortOrder, Map<String, String> filters) {
		// En este ejemplo básico no utilizamos ni el sort ni los filtros
		if (parametros == null) {
			parametros = new HashMap<String, Object>();
		} 
                //Obtenemos los anuncios que ocupen la posicion first hasta pageSize
		anuncios = servicioAnuncios.ejecutaNamedQuery(namedQuery, parametros,
				first, pageSize);
		//Indicamos el número total de registros de la consulta (para poder establecer el número de páginas)
		setRowCount(servicioAnuncios.obtenTotalRegistros(
				countQuery, parametros));
		if (anuncios != null) {
			return anuncios;
		} else {
			return new ArrayList<Anuncio>();
		}
	}
 
 
	@Override
	public Anuncio getRowData(String rowKey) {
		for (Anuncio an : anuncios) {
			if (Long.toString(an.getId()).equals(rowKey))
				return an;
		}
 
		return null;
	}
 
}

DataGrid

http://www.primefaces.org/showcase/ui/datagrid.jsf

DataScroller

Este componente muestra una tabla con ScrollVertical infinito con carga dinámica. El funcionamiento es similar a la carga de listados en Twitter o Facebook. Se precargan N valores y conforme se hace Scroll se cargan el resto.

— Falta link —

DataList

http://www.primefaces.org/showcase/ui/dataListUnordered.jsf

FeedReader

http://www.primefaces.org/showcase/ui/feedReader.jsf

Este componente nos permite leer un rss dado y formatear por pantalla el resultado final que presentaremos al usuario.

<p:feedReader id="rss" value="#{manejadorPrimeRss.rss}" var="feed">
   <br/>
   <h:outputText value="#{feed.title}" style="font-weight: bold;" />
   <p:separator />
   <h:outputText value="#{feed.description.value}" escape="false"/>
   <p:spacer width="25"/>
</p:feedReader>

Gmap

http://www.primefaces.org/showcase/ui/gmapHome.jsf

Este componente pinta un mapa de google en nuestra aplicación y nos permite interactuar con él. Así pues podemos dar de alta marcadores o consultar su información.

Para que el mapa se muestre hay que añadir el siguiente código a la cabecera de nuestra página web:

	<script src="http://maps.google.com/maps/api/js?sensor=false"
			type="text/javascript"></script>

Importante: El componente aún no funciona correctamente (pendiente de estabilizar). Podemos utilizarlo para visualizar marcadores puestos y consultar la información que hayamos dispuesto para ellos, pero no nos permite insertar uno e inmediatamente consultar la información introducida. No se puede hacer un “update” del componente ya que desaparece, y el bean de respaldo sobre el que se basa ha de tener ámbito de Sesión.

El código a continuación mostrado combina la visualización de la información con la introducción de la misma, pero actualmente no funcionan ambas cosas a la vez.

	<p:gmap id="map" center="38.022860,-1.174731" zoom="17"
		type="HYBRID" model="#{manejadorComponentesInput.modeloMapa}"
		widgetVar="map" style="width:600px;height:400px"
		onPointClick="preparaMarcador(event);" streetView="true">
	<p:ajax event="pointSelect" listener="#{manejadorComponentesInput.onMapPointSelect}"
			oncomplete="crearMarcadorDialog.show()" />
	<p:ajax event="overlaySelect" listener="#{manejadorComponentesInput.onMarkerSelect}"/>
		<!-- Ventana de informacion  -->
		<p:gmapInfoWindow>
			<p:outputPanel style="text-align:center;display:block;margin:auto;">
         			<h:outputText value="-#{manejadorComponentesInput.marcadorSeleccionado.title}-" />
			</p:outputPanel>
		</p:gmapInfoWindow>
	</p:gmap>

OverlayPanel

http://www.primefaces.org/showcase/ui/overlayPanel.jsf

Componentes de acciones

Button

http://www.primefaces.org/showcase/ui/button.jsf

CommandButton

http://www.primefaces.org/showcase/ui/commandButton.jsf

http://www.primefaces.org/showcase/ui/commandLink.jsf

Botón para realizar una acción contra el servidor. Como novedad, la acción ajax viene configurada como acción por defecto en vez de un submit normal. Este componente permite reRenderizado y procesamiento parcial de los datos. Las propiedades principales son las siguietnes:

  1. process: Permite indicar qué campos se van a procesar, especificados mediante una lista de id separados por espacios. Como elementos especiales están: “@all” procesa todos los componentes, @this procesa solo el botón, @form procesa el formulario que contiene el botón, @none no procesa ningún elemento, @parent: procesa los hijos del elemento padre del botón
  2. update: Permite refrescar los componentes indicados en este campo (lista separada por espacios), para que el componente localice los id que no están en el mismo formulario se debe poner “:” delante del id.

Un ejemplo de código

   <p:commandButton value="#{messages['madet.buscar']}"
		icon="ui-icon-search" id="btnBuscar" ajax="false" update=":formularioResultados" 
                process="@this,panelFiltro" action="#{manejadorAnuncios.buscar()}" />

Componentes de panel

Accordion

http://www.primefaces.org/showcase/ui/accordionPanelHome.jsf

Dialog

http://www.primefaces.org/showcase/ui/dialogHome.jsf

Componente para crear un modalPanel que aparezca como un popUp para que podamos realizar acciones adicionales, consultar datos, utilizar como ayuda, etc…

Dentro de sus propiedades principales podemos destacar:

  1. position: Posición en la pantalla en la que queremos que aparezca el panel.
  2. modal: Con esta opción habilitada el panel bloqueará la pantalla sobre la que aparece, dejando sólo realizar acciones sobre su contenido.
  3. closeable: Habilita un icono en la esquina superior derecha para cerrar el panel.
  4. appendToBody: Posiciona el panel en al final del HTML generado y garantiza su procesamiento.
  5. widgetVar: Nombre del panel para poder realizar acciones javascript sobre él, como mostrarlo u ocultarlo.
<p:dialog id="panelModal" header="Crear comentario" width="500"
			position="top" height="350" widgetVar="panelComentario"
			style="position:fixed;margin-top:10%;" modal="true" closable="true"
			appendToBody="true" resizable="false">
			<h:form id="modalPanelForm">
				<h:outputText value="#{messages['det.coment.text']}"
					styleClass="nombreLabel" />
				<p:editor id="editComentario" width="400"
					value="#{manejadorAnuncios.comentarioSeleccionado.texto}" />
				<div style="position:absolute;bottom:10;width:95%;">					
					<p:commandButton id="btnCancelComent"
						 value="#{messages['value.cancel']}" style="float:right;"
						icon="ui-icon-close" ajax="true" action="true" 
						onclick="panelComentario.hide();"/>
					<p:commandButton value="#{messages['value.save']}" id="btnGuardarComent"
						icon="ui-icon-disk" ajax="true" action="#{manejadorAnuncios.guardarComentario()}" 
						style="float:right;margin-right:15px;"/>
				</div>
			</h:form>
		</p:dialog>
                       ...
                       ...
                       ...
 
       <!-- Llamada al panel -->
       	<p:commandButton id="btnNuevoAnun" icon="ui-icon-document" style="float:right;margin-bottom:10px;"
		value="#{messages['det.coment.nuevo']}"	onclick="panelComentario.show();" />

Fieldset

http://www.primefaces.org/showcase/ui/fieldset.jsf

Layout

http://www.primefaces.org/showcase/ui/layoutHome.jsf

OutputPanel

http://www.primefaces.org/showcase/ui/outputPanel.jsf

Panel

http://www.primefaces.org/showcase/ui/panel.jsf

PanelGrid

http://www.primefaces.org/showcase/ui/panelGrid.jsf

ScrollPanel

http://www.primefaces.org/showcase/ui/scrollPanel.jsf

TabView

http://www.primefaces.org/showcase/ui/tabviewHome.jsf

Toolbar

http://www.primefaces.org/showcase/ui/toolbar.jsf

Componentes de Menú

http://www.primefaces.org/showcase/ui/breadCrumb.jsf

ContextMenu

http://www.primefaces.org/showcase/ui/contextMenu.jsf

http://www.primefaces.org/showcase/ui/menu.jsf

MegaMenu

http://www.primefaces.org/showcase/ui/megaMenu.jsf

http://www.primefaces.org/showcase/ui/menubar.jsf

Este componente nos proporciona una barra de menú horizontal a la que podremos ir añadiéndole opciones. Se comporta de manera idéntica a una barra de menú de cualquier aplicación de escritorio. A la hora de construir el menú podemos diferenciar los siguientes elementos:

  1. menuitem: Representa un elemento del menú simple, sin hijos, al hacer click sobre él se ejecutará una acción.
  2. submenu: Representa un elemento del menú, compuesto por varios menuitem, que tiene hijos (submenú). Al poner el ratón sobre él se desplegará mostrando los menuitem que contiene.
  3. options: A traves de un “f:facet” especificamos un grupo de elementos que irá alineado a la derecha del todo.

Un ejemplo de código es el siguiente:

      <p:menubar id="menuBar">
		<p:menuitem value="#{messages['value.link.home']}" icon="ui-icon-home"
			url="/paginas/home.xhtml" />
 
		<p:submenu label="#{messages['value.link.comp']}"
			icon="ui-icon-document">
			<p:menuitem value="#{messages['value.link.madet']}"
				url="/paginas/maestroAnuncios.xhtml" icon="ui-icon-newwin"/>
 
		</p:submenu>
 
		<f:facet name="options">
			<p:commandButton value="Salir" icon="ui-icon-extlink"
				action="#{identity.logout()}" />
		</f:facet>
	</p:menubar>

PanelMenu

http://www.primefaces.org/showcase/ui/panelMenu.jsf

Componentes multimedia

DynaImage

http://www.primefaces.org/showcase/ui/dynamicImage.jsf

Galleria

http://www.primefaces.org/showcase/ui/galleria.jsf

Media

http://www.primefaces.org/showcase/ui/media.jsf

http://www.primefaces.org/showcase/ui/lightBox.jsf

Componentes de Avisos

ConfirmDialog

http://www.primefaces.org/showcase/ui/confirmDialog.jsf

Messages

http://www.primefaces.org/showcase/ui/messages.jsf

Componente para mostrar los mensajes faces que produzca el sistema. Creará una barra superior que agrupará los mensajes del mismo tipo en ella. Fundeweb por defecto lo incorpora en sus templates, por lo tanto no es necesario introducirlo en las aplicaciones desarrolladas con este framework.

     <p:messages id="messages" showDetail="false" closable="true"/>

Miscelánea

BlockUI

http://www.primefaces.org/showcase/ui/blockUI.jsf

Permite bloquear componentes jsf cuando se está ejecutando ajax.

Captcha

http://www.primefaces.org/showcase/ui/captchaHome.jsf

Collector

http://www.primefaces.org/showcase/ui/collector.jsf

Permite gestionar colecciones en cliente (crear, eliminar). Minimiza las llamadas al servidor.

Focus

http://www.primefaces.org/showcase/ui/focus.jsf

HotKey

http://www.primefaces.org/showcase/ui/hotkey.jsf

Liga las teclas al cliente para realizar acciones.

ProgressBar

http://www.primefaces.org/showcase/ui/progressBar.jsf

Sticky

http://www.primefaces.org/showcase/ui/sticky.jsf

Este componente permite que un elemento de nuestra página siempre quede visible aunque esta tenga scroll vertical. Alinea el componente en el top de la pantalla y lo mantiene visible en todo momento.

Tooltip

http://www.primefaces.org/showcase/ui/tooltipHome.jsf

FAQ

El editor de primefaces p:editor, aparece deshabilitado cuando lo introduzco en un panel y lo visualizo

Cuando introducimos el componente editor en un panel, a la hora de pintar el panel, lo hacemos normalmente por javascript, por lo tanto el editor no se encuentra preparado para la edición cuando se pinta, para ello debemos actualizar el formulario que contiene dicho editor.

      <p:commandButton id="btnNuevoCom" icon="ui-icon-document"
        	ajax="true" action="#{manejadorAnuncios.creaNuevoComentario()}"
		value="#{messages['det.coment.nuevo']}"
		oncomplete="panelComentario.show();" update=":modalPanelForm"/>

En este código se muestra cómo se pinta el panel desde un botón. En el “oncomplete” nos encargamos de pintar el panel mediante javascript, la modificación importante es update=“:modalPanelForm” ahí es donde indicamos que actualice el formulario, que en este caso contiene el editor.

Cómo obtener el clientID de un componente

A la hora de hacer un update y que nos repinte un componente, debemos poner el clientID, bien en ruta relativa o absoluta. El problema surge porque ese ID no es siempre la cadena de ID de los contenedores ya que en JSF no todos los elementos que pueden agrupar contenido son considerados contenedores, lo que nos plantea problemas a la hora de saber exactamente cuál es la ruta de ID's hasta un determinado componente.

La solución a este problema es crear un <h:outputText> al miso nivel del componente que queremos conocer su ID, en el campo value, ponemos lo siguiente #{component.clientId}.

Tras hacer esto lanzamos otra vez la página y se nos imprimirá en pantalla, donde pusimos el <h:outputText>, la ruta de ID's hasta ese campo, incluido su ID. Una vez impreso, copiamos esa ruta y sustituimos el ID por el de nuestro componente.