You are on page 1of 33

6

Creación de la lista de
contactos con sus detalles
En este capítulo, vamos a desarrollar la parte de administración de contactos de nuestra
aplicación, que incluyen listar, agregar, editar y eliminar contactos, todos al estilo Ajax.

Por otra parte, vamos a aprender nuevos conceptos sobre el marco de componentes
RichFaces y Ajax.

El diseño principal
Vamos a iniciar la preparación del espacio para las principales características de la aplicación.
Como hemos visto en el capítulo 4, la aplicación, queremos un diseño en tres columnas para
los grupos, la lista de contactos y los detalles del contacto. Vamos a abrir el archivo
home.xhtml y agregamos un panel de cuadrículas de tres columnas en el interior del cuerpo:

<h:panelGrid columns="3"
width="100%"
columnClasses="main-group-column, main-contacts-listcolumn,
main-contact-detail-column">
</h:panelGrid>

Estamos utilizando tres nuevas clases CSS (uno para cada columna). Vamos a abrir el archivo
/view/stylesheet/theme.css y agregamos el siguiente código:

.main-group-column {
width: 20%;
vertical-align: top;
}
.main-contacts-list-column {
width: 40%;
vertical-align: top;
}
.main-contact-detail-column {
width: 40%;
vertical-align: top;
}

Las columnas principales están listas, y ahora queremos dividir el contenido de cada columna
en un archivo aparte (para no tener un archivo grande y difícil de leer) usando las capacidades
de plantillas de Facelets, vamos a crear una nueva carpeta dentro de la carpeta /view llamada
main y vamos a crear los siguientes archivos vacíos en su interior:
• contactsGroups.xhtml
• contactsList.xhtml
• contactEdit.xhtml
• contactView.xhtml

Ahora vamos a abrirlos y escribir el código estándar para un archivo vacío (included):

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0


Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
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:rich="http://richfaces.org/rich"
xmlns:a="http://richfaces.org/a4j">
<!-- my code here -->
</ui:composition>

Ahora, tenemos todas las piezas listas para ser incluido en el archivo home.xhtml, vamos a
abrirlo y empezar a añadir la primera columna dentro de h:panelGrid:

<a:outputPanel id="contactsGroups">
<ui:include src="main/contactsGroups.xhtml"/>
</a:outputPanel>

Como puede ver, hemos encerrado include con un a:outputPanel que se utiliza como
marcador de posición para la nueva presentación.

Incluir una etiqueta Facelets (ui:include) dentro de a:outputPanel que hemos utilizado
con el fin de incluir la página en ese punto.

Marcadores de posición de Ajax


Un concepto muy importante a tener en cuenta durante el desarrollo es que el marco Ajax no
puede agregar o eliminar, sólo puede reemplazar los elementos existentes en la página. Por
esta razón, si desea añadir algo de código, usted necesita utilizar un marcador de posición.

RichFaces tiene un componente que puede ser utilizado como un marcador de posición
a4j:outputPanel.

Dentro de a4j:outputPanel, puede poner otros componentes que utilizan atributos


"rendered" con el fin de decidir si son visibles o no. Cuando quiera volver a representar a todos
los componentes incluidos, sólo recosntruir la outputPanel, y todos funcionan sin ningún
problema.

Éste no es un fragmento de código que trabaja:

<h:form>
<h:inputText value="#{aBean.myText}">
<a4j:support event="onkeyup" reRender="out1" />
</h:inputText>
</h:form>
<h:outputText
id="out1"
value="#{aBean.myText}"
rendered="#{not empty aBean.myText}"/>

Este código parece ser el mismo que el ejemplo de a4j:support, pero no funciona.

El problema es que hemos añadido el atributo rendered a outputText, por lo que


inicialmente, out1 no serán rendered (ya que la propiedad de texto está vacío inicialmente
rendered y será igual a false). Después de la respuesta del Ajax, el motor de JavaScript no
se encuentra el out1 elemento (que no está en la página debido rendered="false"), y no
podrá actualizarlo (recuerde que usted no puede añadir o eliminar elementos , sólo sustituirlos).

Es muy simple de hacer que el código funcione:

<h:form>
<h:inputText value="#{aBean.myText}">
<a4j:support event="onkeyup" reRender="out2" />
</h:inputText>
</h:form>
<a4j:outputPanel id="out2">
<h:outputText
id="out1"
rendered="#{not empty aBean.myText}"
value="#{aBean.myText}" />
</a4j:outputPanel>

Como puede ver, sólo tienes que poner el componente out1 dentro A4j:outputPanel
(llamado out2) y decirle a4j:support que visualice out2 en lugar de out1.

Inicialmente, out2 será visualizado, pero estará vacío (porque out1 no se muestra). Después
de la respuesta de Ajax, el out2 vacío serán sustituidos por elementos de marcado que
también contienen el componente de out1 (que ahora es visible, porque la propiedad myText
no está vacía después de la actualización del Ajax y la propiedad rendered es verdadero).

Un concepto muy importante a tener en cuenta durante el desarrollo es que el marco Ajax no
puede agregar o eliminar, sólo puede reemplazar los elementos existentes de la página. Por
esta razón, si desea añadir algo de código, usted necesita utilizar un marcador de posición.

En el ejemplo de la lista de contactos del Capítulo 3, Primeros Pasos, no usamos un marcador


de posición para el contacto no encontrado h:outputText "No se han encontrado", porque
los componentes de la acción Ajax (como el botón "Borrar") reconstruye el formulario que la
rodea fContactsList que actúa como un marcador de posición en este caso.

El cuadro de los grupos


Esta casilla contendrá todos los grupos de contactos, para que el usuario sea capaz de
organizar los contactos en diferentes grupos de una mejor manera.

No vamos a implementar las funcionalidades del cuadro de grupo en este capítulo. Por lo tanto,
por ahora la columna de grupo es sólo un rich:panel con un enlace para actualizar la lista
de contactos.

Vamos a abrir el archivo contactsGroups.xhtml e insertemos el siguiente código:

<h:form>
<rich:panel>
<f:facet name="header">
<h:outputText value="#{messages['groups']}" />
</f:facet>
<h:panelGrid columns="1">
<a:commandLink value="#{messages['allContacts']}"
ajaxSingle="true"
reRender="contactsList">
<f:setPropertyActionListener value="#{null}"
target="#{homeContactsListHelper.contactsList}" />
</a:commandLink>
</h:panelGrid>
</rich:panel>
</h:form>

Como puede ver, hemos puesto tres columnas h:panelGrid (que se utilizará en el futuro) y
a:commandLink, que sólo establece la propiedad de contactos del bean
homeContactListHelper (que veremos en la siguiente sección) a nula, a fin de que la lista
se lea nuevamente. Al final la interacción de Ajax, volverá a hacer la columna de contactos a fin
de mostrar los nuevos datos.

También, observe que todavía estamos dando soporte a todos los mensajes de texto
utilizando la propiedad messages, la tarea de llenar el archivo de messages_XX.properties
se deja como ejercicio para el usuario.

La lista de contactos
La segunda columna en el interior h:panelGrid de home.xhtml se ve algo como:

<a:outputPanel id="contactsList">
<ui:include src="main/contactsList.xhtml"/>
</a:outputPanel>

En cuanto a los grupos, se utilizó un marcador de posición que rodean la interfaz de usuario, la
etiqueta ui:include.

Ahora vamos a centrarnos en la creación de la tabla de datos, abra el archivo


/view/main/contactsList.xhtml y añadir el fragmento de código para el primer
DataTable:

<h:form>
<rich:dataTable id="contactsTable"
reRender="contactsTableDS"
rows="20"
value="#{homeContactsListHelper.contactsList}" var="contact">
<rich:column width="45%">
<h:outputText value="#{contact.name}"/>
</rich:column>
<rich:column width="45%">
<h:outputText value="#{contact.surname}"/>
</rich:column>
<f:facet name="footer">
<rich:datascroller id="contactsTableDS"
for="contactsTable"
renderIfSinglePage="false"/>
</f:facet>
</rich:dataTable>
<h:outputText value="#{messages['noContactsInList']}"
rendered="#{homeContactsListHelper.contactsList.size()==0}"/>
</h:form>

Acabamos de añadir el componente rich:dataTable con algunas columnas y una barra de


desplazamiento de datos Ajax al final.

Las diferencias entre h:dataTable y rich:dataTable


RichFaces ofrece su propia versión de h:dataTable, que contiene más funciones y se
integra mejor en el marco de RichFaces.

La primera característica adicional importante, de hecho, es el soporte de la skinnability


siguiendo los estándares de RichFaces.

Otras características son de fila y columna con soporte extendido (lo comentaremos en la
sección columnas y grupos de columnas), fuera de la caja de filtro y ordenación (que se
examinan en la sección de filtrado y ordenación), más los controladores de eventos de
JavaScript (como onRowClick, onRowContextMenu, onRowDblClick, y así
sucesivamente) y el atributo reRender.

Al igual que otros componentes de iteración de datos el marco RichFaces, también da soporte
a la actualización parcial de la fila (véase el Capítulo 10, Técnicas avanzadas para más
información).

Paginación de datos
La implementación de paginación de datos en Ajax usando RichFaces es muy simple, sólo hay
que decidir el número de filas que se mostrarán en cada página mediante el establecimiento de
atributo rows de DataTable (en nuestro caso, hemos elegido 20 filas por página), y despues
“adjuntamos” al componente rich:datascroller para llenar el atributo for con el ID de
DataTable:

<rich:datascroller id="contactsTableDS"
for="contactsTable"
renderIfSinglePage="false"/>

Aquí puedes ver otro atributo muy útil (renderIfSinglePage) que oculta el componente
cuando sólo hay una sola página en la lista (esto significa que la lista contiene un número de
elementos menor o igual al valor del atributo rows) .

Hay que tener en cuenta que el componente rich:datascroller debe permanecer dentro
del componente de formulario (h:form o a:form) para que funcione.

Es posible la personalización de componentes rich:datascroller no solo mediante el uso


de clases CSS (como siempre), sino también personalizando nuestras propias piezas utilizando
los siguientes facets:

• pages
• controlsSeparator
• first, first_disabled
• last, last_disabled
• next, next_disabled
• previous, previous_disabled
• fastforward, fastforward_disabled
• fastrewind, fastrewinf_disabled

He aquí un ejemplo con algunos facets personalizados (mediante el uso de cadenas):

<rich:datascroller id="contactsTableDS" for="contactsTable"


renderIfSinglePage="false">
<f:facet name="first">
<h:outputText value="First" />
</f:facet>
<f:facet name="last">
<h:outputText value="Last" />
</f:facet>
</rich:datascroller>

Aquí está el resultado:

Usted puede usar una imagen (u otro componente) en lugar de texto, con el fin de crear su
scroller a su medida.

Otro ejemplo interesante es la siguiente:

<rich:datascroller id="contactsTableDS" for="contactsTable"


renderIfSinglePage="false">
<f:facet name="first">
<h:outputText value="First"/>
</f:facet>
<f:facet name="last">
<h:outputText value="Last"/>
</f:facet>
<f:attribute name="pageIndexVar"
value="pageIndexVar"/>
<f:attribute name="pagesVar"
value="pagesVar"/>
<f:facet name="pages">
<h:panelGroup>
<h:outputText value="Page #{pageIndexVar} / #{pagesVar}"/>
</h:panelGroup>
</f:facet>
</rich:datascroller>

El resultado es:

Al establecer los atributos de pageIndexVar y pagesVar, somos capaces de utilizarlos en un


componente outputText, como lo hemos hecho en el ejemplo.
Una atributo útil de este componente es maxpages que establece el número máximo de
páginas de enlaces (los números en el medio), que muestra el scroller-por lo tanto, podemos
controlar el tamaño de la misma.

El atributo page podría estar vinculado a una propiedad de un bean, con el fin de cambiar a
una página colocando el número - un caso de uso simple podría ser utilizando un inputText y
un CommandButton, a fin de que el cliente introduzca el número de página que el/ella quieran.

Aquí está el código que muestra cómo implementarlo:

<rich:datascroller
for="contactsList" maxPages="20" fastControls="hide"
page="#{customDataScrollerExampleHelper.scrollerPage}"
pagesVar="pages"
id="ds">
<f:facet name="first">
<h:outputText value="First" />
</f:facet>
<f:facet name="first_disabled">
<h:outputText value="First" />
</f:facet>
<f:facet name="last">
<h:outputText value="Last" />
</f:facet>
<f:facet name="last_disabled">
<h:outputText value="Last" />
</f:facet>
<f:facet name="previous">
<h:outputText value="Previous" />
</f:facet>
<f:facet name="previous_disabled">
<h:outputText value="Previous" />
</f:facet>
<f:facet name="next">
<h:outputText value="Next" />
</f:facet>
<f:facet name="next_disabled">
<h:outputText value="Next" />
</f:facet>
<f:facet name="pages">
<h:panelGroup>
<h:outputText value="Page "/>
<h:inputText
value="#{customDataScrollerExampleHelper.
scrollerPage}"
size="4">
<f:validateLongRange minimum="0" />
<a:support event="onkeyup" timeout="500"
oncomplete="#{rich:component('ds')}.
switchToPage(this.value)" />
</h:inputText>
<h:outputText value=" of #{pages}"/>
</h:panelGroup>
</f:facet>
</rich:datascroller>
Como puede ver, además de personalizar el texto de las secciones First, Last, Previous, y
Next. A continuación, se define un facet pages mediante la inserción de h:inputText
conectado con un valor entero dentro de un bean de respaldo. También hemos añadido la
etiqueta a:support, con objeto de reducir el cambio de página después del evento keyup se
ha completado. También hemos establecido el atributo de timeout, para llamar al servidor
cada 500 ms y no cada vez que el usuario escriba

Usted puede ver una captura de pantalla aquí:

Agregar encabezados de la columna


Ahora nos gustaría agregar un encabezado para cada columna de la DataTable, la forma
más sencilla es simplemente poner una faceta dentro del componente rich:column, de esta
manera:

<rich:column>
<f:facet name="header">
<h:outputText value="my header" />
</f:facet>
...
</rich:column>

Este método también funciona para el componente estándar h:dataTable, RichFaces mejora
las capacidades de la tabla de la partida, al permitir la agrupación, mediante el componente
rich:columnGroup.

Por lo tanto, volviendo a nuestra aplicación, podemos poner el siguiente código dentro de la
etiqueta rich:dataTable a fin de definir el encabezado de la dataTable:

<rich:dataTable ... >


<f:facet name="header">
<rich:columnGroup>
<rich:column colspan="2">
<h:outputText value="Contacts"/>
</rich:column>
<rich:column breakBefore="true">
<h:outputText value="Name"/>
</rich:column>
<rich:column>
<h:outputText value="Surname"/>
</rich:column>
</rich:columnGroup>
</f:facet>
...

Y el resultado será el siguiente:


Columnas y grupos de columnas
Con la versión de RichFaces es también muy conveniente extender el comportamiento
dataTable para visualizar la fila.

Echemos una versión simplificada (sin header, footer y datascroller) de la tabla


contactsList:

<rich:dataTable id="contactsTable"
value="#{homeContactsListHelper.contactsList}" var="contact">
<rich:column>
<h:outputText value="#{contact.name}"/> </rich:column>
<rich:column>
<h:outputText value="#{contact.surname}"/> </rich:column>
<rich:column>
<a:commandButton image="/img/view.png" /> </rich:column>
</rich:dataTable>

Se trata de un dataTable normal y luce así:

Ahora vamos a editar y agregar dos atributos Span y breakBefore:

<rich:dataTable id="contactsTable"
value="#{homeContactsListHelper.contactsList}"
var="contact">
<rich:column colspan="2">
<h:outputText value="#{contact.name}"/>
</rich:column>
<rich:column breakBefore="true">
<h:outputText value="#{contact.surname}"/>
</rich:column>
<rich:column>
<a:commandButton image="/img/view.png" />
</rich:column>
</rich:dataTable>

Con las características mencionadas, como se ve:


¿Qué ha sucedido?

Les hemos dicho a la primera columna de "span" (usted puede conocer el significado porque es
un atributo estándar de una columna de tablas HTML), dos columnas y el segundo "break
before", en el sentido de cierre de la fila (colocar el código HTML etiqueta </ tr>).

Así, la primera columna llena el espacio de dos columnas y la segunda se visualiza en la otra
fila, sencillo, ¿no?

También puede utilizar el atributo rowspan con el fin de abarcar filas en lugar de columnas,
como estándar para las tablas HTML.

Podemos tener el mismo resultado utilizando un componente rich:columnGroup en lugar del


atributo breakBefore, como en el ejemplo siguiente:

<rich:dataTable id="contactsTable"
value="#{homeContactsListHelper.contactsList}"
var="contact">
<rich:column colspan="2">
<h:outputText value="#{contact.name}"/>
</rich:column>
<rich:columnGroup>
<rich:column>
<h:outputText value="#{contact.surname}"/>
</rich:column>
<rich:column>
<a:commandButton image="/img/view.png"/>
</rich:column>
</rich:columnGroup>
</rich:dataTable>

Como podemos ver, el resultado es exactamente el mismo.

Otro uso de rich:column y rich:columnGroup es definir un complejo encabezado de la


tabla como lo hemos hecho en nuestra aplicación, como se muestra en la sección anterior.

rich:column contiene más atributos muy útiles que span, breakBefore, filtrado y
ordenación de los atributos (que vamos a ver en la siguiente sección), que no encontramos en
el componente estándar h:column.

Por ejemplo, en nuestra aplicación, usamos el atributo width con el fin de establecer el ancho
para cada columna, sin necesidad de utilizar una clase CSS sólo para eso.
Característica de filtrado y ordenación
Otra característica importante que hemos visto en un ejemplo sencillo en el capítulo 3 es la de
que fuera de la caja hay controles de filtrado y ordenación que el componente
rich:dataTable proporciona.

Con el fin de añadir esta función a nuestra tabla, vamos a editar la etiqueta rich:column para
el nombre y apellido, como se muestra en el siguiente código:

<rich:column width="45%"
sortBy="#{contact.name}"
filterBy="#{contact.name}">
<h:outputText value="#{contact.name}"/>
</rich:column>
<rich:column width="45%"
sortBy="#{contact.surname}"
filterBy="#{contact.surname}">
<h:outputText value="#{contact.surname}"/>
</rich:column>

Usted tendrá la característica de filtro y ordenación para su tabla sólo agregando estos dos
atributos.

En el capítulo 10, vamos a explicar de una manera más personalizada para la administración
de filtrado y ordenación.

La barra de herramientas inferior


Tenemos una barra de herramientas en la parte inferior de la tabla que contiene botones de
acción para los diferentes tipos de acción (vamos a agregar el botón para añadir un nuevo
contacto en la siguiente sección).

Hemos visto a el componente rich:toolbar de la barra de herramientas en el capítulo 5, la


estructura de aplicación, por lo que sólo tiene que añadir este código después del código para
rich:datascroller:

<rich:toolBar>
<rich:toolBarGroup>
<!-- my action buttons here -->
</rich:toolBarGroup>
</rich:toolBar>
El bean de respaldo
Hemos visto la conexión de la tabla con un bean de apoyo llamado
homeContactsListHelper, vamos a crearla!

Vamos a crear un nuevo paquete llamado main dentro de


book.richfaces.advcm.modules, y crear una nueva clase llamada
HomeContactsListHelper dentro de él.

El componente bean es muy simple, ya que acaba de recuperar la lista de contactos de la base
de datos (los grupos no son administrados por ahora) y podría tener el siguiente aspecto:

@Name("homeContactsListHelper")
@Scope(ScopeType.CONVERSATION)
public class HomeContactsListHelper {
@In(create=true)
EntityManager entityManager;
@In(required = true)
Contact loggedUser;
private List<Contact> contactsList;
public List<Contact> getContactsList() {
if (contactsList ==null) {
// Creating the query
String query="from Contact c where c.contact.id=:fatherId";
// Getting the contacts list
contactsList = (List<Contact>)
entityManager.createQuery(query)
.setParameter("fatherId",
loggedUser.getId())
.getResultList();
}
return contactsList;
}
public void setContactsList(List<Contact> contactsList) {
this.contactsList = contactsList;
}

Para resumir, la anotación @Name define el nombre del componente Seam / JSF del bean de
respaldo, @Scope define el alcance del componente, se inyecta (con la anotación @In), el
componente entityManager (para consultar la base de datos con JPA) y la instancia de
contacto referenciando al usuario conectado durante la fase de inicio de sesión.

Además, el bean tiene una propiedad llamada contactsList que es lentamente inicializado
en el método getContactsList() consultando la base de datos.

Como estamos utilizando el ámbito de la conversación, nos gustaría iniciar la conversación


para entrar en la página de inicio. Hay diferentes maneras de hacer esto en nuestro caso,
vamos a abrir el archivo /view/home.page.xml y agregamos el siguiente contenido:

<begin-conversation join="true" />

Así que, ahora, cuando el usuario acceda a la página de inicio, una nueva conversación se
crea si no hay ninguno. Si no, la existente se mantendrá (join = "true").
Los detalles del contacto
Para la tercera columna, nos gustaría mostrar tres estados diferentes:

El mensaje "contacto no seleccionado" cuando no se ha seleccionado ningún contacto (para


que la propiedad sea null).

Una vista de la caja sólo cuando no estamos en el modo de edición (la propiedad
selectedContactEditing se establece en false)

Un cuadro de edición cuando estamos en el modo de edición (la propiedad


selectedContactEditing se establece a true)

Por lo tanto, vamos a abrir la página home.xhtml e insertaremos la tercera columna dentro de
la cuadricula del panel con los tres estados:

<a:outputPanel id="contactDetail">
<a:outputPanel rendered="#{homeSelectedContactHelper.
selectedContact==null}">
<rich:panel>
<h:outputText
value="#{messages['noContactSelected']}"/>
</rich:panel>
</a:outputPanel>
<a:outputPanel
rendered="#{homeSelectedContactHelper.
selectedContact!=null and
homeSelectedContactHelper.
selectedContactEditing==false}">
<ui:include src="main/contactView.xhtml"/>
</a:outputPanel>
<a:outputPanel
rendered="#{homeSelectedContactHelper.
selectedContact!=null and
homeSelectedContactHelper.
selectedContactEditing==true}">
<ui:include src="main/contactEdit.xhtml"/>
</a:outputPanel>
</a:outputPanel>

Aquí, hemos puesto la principal etiqueta a:outputPanel como el marcador de posición


principal y dentro ponemos tres casos más de a:outputPanel (uno por cada estado) con el
atributo presenta a fin de decidir cuál de ellas se va a mostrar.

La primera de ellas sólo muestra un mensaje cuando se establece en null


homeSelectedContactHelper. selectedContact:

La segunda instancia de a:outputPanel incluirá el archivo main/contactView.xhtml


sólo si homeSelectedContactHelper.selectedContact no es null y no estamos en
modo de edición (para homeSelectedContactHelper.selectedContactEditing se
establece en false), la tercera sólo se mostrarán si
homeSelectedContactHelper.selectedContact no es nulo, y estamos en el modo de
edición (es decir homeSelectedContactHelper.selectedContactEditing es igual a
true).

Antes de empezar a escribir las secciones include, vamos a ver cómo el bean principal para el
contacto seleccionado se vé y conectarlo con la tabla de datos para seleccionar el contacto de
éste.

El bean de soporte
Vamos a crear una nueva clase llamada HomeSelectedContactHelper dentro del paquete
book.richfaces.advcm.modules.main, la clase puede ser algo como esto:

@Name("homeSelectedContactHelper")
@Scope(ScopeType.CONVERSATION)
public class HomeSelectedContactHelper {
@In(create = true)
EntityManager entityManager;
@In(required = true)
Contact loggedUser;
@In
FacesMessages facesMessages;
// My code here
}

Este es un componente estándar de JBoss Seam, como hemos visto en los otros capítulos y
ahora vamos a añadir nuestras propiedades.

El bean que vamos a utilizar para ver y editar las características es muy sencillo de entender ya
que sólo contiene dos propiedades (es decir, selectedContact y
selectedContactEditing) y algunos métodos de acción para su administración.

Vamos a añadir las propiedades a nuestra clase:

private Contact selectedContact;


private Boolean selectedContactEditing;

public Contact getSelectedContact() {


return selectedContact;
}
public void setSelectedContact(Contact selectedContact) {
this.selectedContact = selectedContact;
}
public Boolean getSelectedContactEditing() {
return selectedContactEditing;
}
public void setSelectedContactEditing(Boolean
selectedContactEditing) {
this.selectedContactEditing = selectedContactEditing;
}

Como puede ver, simplemente hemos añadido dos propiedades con el estándar getter y setter.

Vamos a ver ahora los métodos de acción:

public void createNewEmptyContactInstance() {


setSelectedContact(new Contact());
}
public void insertNewContact() {
// Attaching the owner of the contact
getSelectedContact().setContact(loggedUser);
entityManager.persist(getSelectedContact());
facesMessages.addFromResourceBundle(StatusMessage.Severity.INFO,
"contactAdded");
}
public void saveContactData() {
entityManager.merge(getSelectedContact());
facesMessages.addFromResourceBundle(StatusMessage.Severity.INFO,
"contactSaved");
}
public void deleteSelectedContact() {
entityManager.remove(getSelectedContact());
// De-selecting the current contact
setSelectedContact(null);
setSelectedContactEditing(null);
facesMessages.addFromResourceBundle(StatusMessage.Severity.INFO,
"contactDeleted");
}
public boolean isSelectedContactManaged() {
return getSelectedContact() != null && entityManager.contains(getS

electedContact());
}

No es difícil entender lo que hacen, sin embargo, con el fin de ser claros, vamos a describir lo
que cada método hace.

El método createNewEmptyContactInstance() simplemente establece la propiedad


selectedContact con una nueva instancia de la clase de Contact será llamado por "Añadir
contacto".

Después de que el usuario ha hecho clic en "Añadir contacto" se incluirán los datos de
contacto, él / ella ha de persistir esta nueva instancia de los datos en la base de datos. Se lleva
a cabo por el insertNewContact () método, llamado cuando se hace clic en el botón Insertar.

Si el usuario edita un contacto y haga clic en el botón "Guardar", el método


saveContactData() será llamado, con el fin de almacenar las modificaciones en la base de
datos.

Al igual que al guardar, el método deleteSelectedContact ()será llamado por el botón


"Eliminar", a fin de eliminar la instancia de la base de datos.

Una mención especial para el método isSelectedContactManaged()que se utiliza para


determinar si la propiedad selectedContact contiene un bean que existe en la base de
datos (así, estoy editando), o una nueva instancia aún no persistió a la base de datos. Lo
utilizamos sobre todo en propiedades rendered, a fin de determinar qué componente para
mostrar (podrás ver esto en la sección siguiente).

Seleccionar el contacto en la lista de contactos


Vamos a utilizar la lista de contactos a fin de decidir que el contacto se debe demostrar en la
vista de detalle.
La forma más sencilla es añadir una nueva columna en el DataTable, y poner un botón de
comando (o enlace) para seleccionar el bean con el fin de visualizar la vista de detalle. Vamos
a abrir el archivo contactsList.xhtml y añadir otra columna como sigue:

<rich:column width="10%" style="text-align: center">


<a:commandButton image="/img/view.png"
reRender="contactDetail">
<f:setPropertyActionListener value="#{contact}"
target="#{homeSelectedContactHelper.selectedContact}"/>
<f:setPropertyActionListener value="#{false}"
target="#{homeSelectedContactHelper.selectedContactEditing}"/>
</a:commandButton>
</rich:column>

Dentro de la columna, hemos añadido el compponente a:commandButton (que muestra una


imagen en lugar del texto estándar) que no requiere ninguna acción que utiliza el método
f:setPropertyAction para establecer el valor
homeSelectedContactHelper.selectedContact a el contacto con (el valor de fila de la
DataTable), y para decirle que muestre el cuadro de vista y no la edición (configuración
homeSelectedContactHelper.selectedContactEditing a false).

Después de la llamada Ajax, se volverá a hacer el cuadro de contactDetail con el fin de


reflejar el cambio.

Además, el encabezado debe ser cambiado para reflejar la columna de añadir:

<rich:dataTable ... >


<f:facet name="header">
<rich:columnGroup>
<rich:column colspan="3">
<h:outputText value="Contacts"/>
</rich:column>
<rich:column breakBefore="true">
<h:outputText value="Name"/>
</rich:column>
<rich:column>
<h:outputText value="Surname"/>
</rich:column>
<rich:column>
<rich:spacer/>
</rich:column>
</rich:columnGroup>
</f:facet>
...

Hemos incrementado el valor del atributo colspan y se agregó una nuevo encabezado (vació)
de la columna.

La nueva lista de contactos se parecerá a la siguiente pantalla:


Añadir un nuevo contacto
Otra característica que nos gustaría añadir a la lista de contactos es el "Añadir contacto". Con
el fin de hacer eso, vamos a usar la barra de herramientas vacía que ponemos en la primera
sección de este capítulo.

Vamos a añadir un botón de acción en el componente rich:toolBar:

<a:commandButton image="/img/addcontact.png"
reRender="contactDetail"
action="#{homeSelectedContactHelper.createNewEmptyContactInstance}">
<f:setPropertyActionListener value="#{true}"
target="#{homeSelectedContactHelper.selectedContactEditing}"/>
</a:commandButton>

Este botón se llame al método de acción


homeSelectedContactHelper.createNewEmptyContactInstance() a fin de crear y
seleccionar una instancia vacía y establecerá
homeSelectedContactHelper.selectedContactEditing a true con el fin de iniciar la
edición, después de las llamadas Ajax, se volverá a reconstruir el cuadro de contactDetail
para reflejar los cambios.

Ver los detalles del contacto


Estamos dispuestos a poner en práctica la opinión de cuadro de contacto detalle, simplemente
abra el archivo /view/main/contactView.xhtml y agregue el siguiente código:

<h:form>
<rich:panel>
<f:facet name="header">
<h:outputText
value="#{homeSelectedContactHelper.selectedContact.name}
#{homeSelectedContactHelper.selectedContact.surname}"/>
</f:facet>
<h:panelGrid columns="2" rowClasses="prop"
columnClasses="name,value">
<h:outputText value="#{messages['name']}:"/>
<h:outputText
value="#{homeSelectedContactHelper.selectedContact.name}"/>
<h:outputText value="#{messages['surname']}:"/>
<h:outputText
value="#{homeSelectedContactHelper.selectedContact.surname}"/>
<h:outputText value="#{messages['company']}:"/>
<h:outputText
value="#{homeSelectedContactHelper.selectedContact.company}"/>
<h:outputText value="#{messages['email']}:"/>
<h:outputText
value="#{homeSelectedContactHelper.selectedContact.email}"/>
</h:panelGrid>
</rich:panel>
<rich:toolBar>
<rich:toolBarGroup>
<a:commandLink ajaxSingle="true"
reRender="contactDetail"
styleClass="image-command-link">
<f:setPropertyActionListener value="#{true}"
target="#{homeSelectedContactHelper.selectedContactEditing}"/>
<h:graphicImage value="/img/edit.png" />
<h:outputText value="#{messages['edit']}" />
</a:commandLink>
</rich:toolBarGroup>
</rich:toolBar>
</h:form>

La primera parte es el componente rich:panel que contiene a h:panelGrid con el detalle


de los campos. En la segunda parte del código, colocamos un rich:toolBar que contiene el
enlace de comandos (con una imagen y un texto) que activa el modo de edición, que de hecho,
sólo se establece la propiedad
homeSelectedContactHelper.selectedContactEditing a true y vuelve a reconstruir
contactDetail con el fin de hacer que aparezca en el cuadro de edición.

También hemos añadido una clase CSS dentro del archivo /view/stylesheet/theme.css
para manejar el diseño de los enlaces de comandos con las imágenes:

.image-command-link {
text-decoration: none;
}
.image-command-link img {
vertical-align: middle;
padding-right: 3px;
}

La vista del cuadro es:


Ahora estamos listos para desarrollar el cuadro de edición.

Edición de los detalles del contacto


Cuando en el modo de edición, el contenido de la vista /view/main/contactEdit.xhtml
se muestra en el cuadro de detalles de contacto, vamos a abrirlo para editarlo. Vamos a añadir
el código para crear el panel principal:

<h:form>
<rich:panel>
<f:facet name="header">
<h:panelGroup>
<h:outputText
value="#{homeSelectedContactHelper.selectedContact.name}
#{homeSelectedContactHelper.selectedContact.surname}"
rendered="#{homeSelectedContactHelper.selectedContactManaged}"/>
<h:outputText
value="#{messages['newContact']}"
rendered="#{!homeSelectedContactHelper.selectedContactManaged}"/>
</h:panelGroup>
</f:facet>
<!-- my code here -->
</rich:panel>
<!-- my code here -->
</h:form>

Este es un panel estándar rich:panel con un encabezado personalizado que tiene dos
componentes h:outputText que se mostrará en función del atributo (si se trata de un nuevo
contacto o no).

Más de un componente dentro de f:facet Recuerde que f:facet debe tener un solo hijo,
por lo que, para poner más de un componente, tienes que usar un h:panelGroup o algo
similar.

En el interior del panel, se van a poner h:panelGrid contiene los componentes para la
edición de datos:

<rich:graphValidator>
<h:panelGrid columns="3" rowClasses="prop"
columnClasses="name,value,validatormsg">
<h:outputLabel for="scName"
value="#{messages['name']}:"/>
<h:inputText id="scName"
value="#{homeSelectedContactHelper.selectedContact.name}"/>
<rich:message for="scName" styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
<h:outputLabel for="scSurname"
value="#{messages['surname']}:"/>
<h:inputText id="scSurname"
value="#{homeSelectedContactHelper.selectedContact.surname}"/>
<rich:message for="scSurname"
styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
<h:outputLabel for="scCompany"
value="#{messages['company']}:"/>
<h:inputText id="scCompany"
value="#{homeSelectedContactHelper.selectedContact.company}"/>
<rich:message for="scCompany"
styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
<h:outputLabel for="scEmail"
value="#{messages['email']}:"/>
<h:inputText id="scEmail"
value="#{homeSelectedContactHelper.selectedContact.email}"/>
<rich:message for="scEmail" styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
</h:panelGrid>
<rich:graphValidator>

Nada complicado aquí, acabamos de utilizar h:outputLabel, h:inputText y


rich:message para todos los propiedades de Contact para ser editado, que aparece como
sigue:

El botón de la barra de herramientas


Al final del panel, nos gustaría poner la barra de herramientas contiene los botones de acción
para la inserción, guardar, cancelar y eliminar el contacto seleccionado que se muestra. Con el
fin de hacer eso, vamos a insertar el código siguiente después de las etiquetas de cierre
rich:panel (y antes de la etiqueta de cierre h:form):

<rich:toolBar> <rich:toolBarGroup>
<!-- my action buttons here -->
</rich:toolBarGroup> </rich:toolBar>

Empecemos por la inserción de los botones de acción para introducir un nuevo contacto:

<a:commandLink reRender="contactsList,contactDetail"
action="#{homeSelectedContactHelper.insertNewContact}"
rendered="#{!homeSelectedCon tactHelper.selectedContactManaged}"
styleClass="image-command-link">
<f:setPropertyActionListener value="#{null}"
target="#{homeContactsListHelper.contactsList}"/>
<h:graphicImage value="/img/insert.png"/>
<h:outputText value="#{messages['insert']}"/>
</a:commandLink>

Este botón persiste el nuevo contacto en la base de datos (llamando al método


homeSelectedContactHelper.insertNewContact()) y, con la propiedad
f:setPropertyActionListener, se establece la propiedad contactsList en null, lo
que la lista se leerá de nuevo desde la base de datos (para que refleje los cambios). Después
de eso, se vuelve a hacer la lista y el cuadro de detalle para reflejar los cambios.

Veamos el código del botón para cancelar la inserción:

<a:commandLink ajaxSingle="true" reRender="contactDetail"


rendered="#{!homeSelectedContactHelper.
selectedContactManaged}"
styleClass="image-command-link">
<f:setPropertyActionListener
value="#{false}"
target="#{homeSelectedContactHelper.
selectedContactEditing}"/>
<f:setPropertyActionListener
value="#{null}"
target="#{homeSelectedContactHelper.
selectedContact}"/>
<h:graphicImage value="/img/cancel.png"/>
<h:outputText value="#{messages['cancel']}"/>
</a:commandLink>

Este botón no llama a cualquier método de acción, sino que sólo establece la propiedad
selectedContact a null y la propiedad selectedContactEditing a false para
"cancelar" la acción de inserción.

Destacamos la propiedad ajaxSingle, esta es una característica muy importantes que se


utilizan para evitar el envío del formulario cuando se pulsa el botón (así, en este caso, cuando
el usuario hace clic en el botón Cancelar, los datos del formulario no se haya presentado con la
solicitud de Ajax) . Lo veremos más a fondo en la final de la sección.

El otro botón que vamos a añadir es el botón Guardar:

<a:commandLink reRender="contactsList,contactDetail"
action="#{homeSelectedContactHelper.saveContactData}"
rendered="#{homeSelectedContactHelper.selectedContactManaged}"
styleClass="image-command-link">
<f:setPropertyActionListener value="#{false}"
target="#{homeSelectedContactHelper.selectedContactEditing}"/>
<f:setPropertyActionListener value="#{null}"
target="#{homeContactsListHelper.contactsList}"/>
<h:graphicImage value="/img/save.png"/>
<h:outputText value="#{messages['save']}"/>
</a:commandLink>

Simplemente guarda la modificación de la propiedad, se establece la propiedad lista de


contactos a null, (así, se leerá de nuevo desde la base de datos), y establece el modo de
edición en false (así, los detalles de contacto se muestra).

El botón Cancelar de un contacto existente es casi el mismo que el de los nuevos contactos:

<a:commandLink ajaxSingle="true" reRender="contactDetail"


rendered="#{homeSelectedContactHelper.selectedContactManaged}"
styleClass="image-command-link">
<f:setPropertyActionListener value="#{false}"
target="#{homeSelectedContactHelper.selectedContactEditing}"/>
<h:graphicImage value="/img/cancel.png"/>
<h:outputText value="#{messages['cancel']}"/>
</a:commandLink>

La única diferencia es que no establece el selectedContact a null como nos gustaría ver
el contacto en el modo de vista después de cancelar la edición. El último botón que vamos a
introducir es la de la eliminación:

<a:commandLink ajaxSingle="true"
reRender="contactDetail,contactsList"
action="#{homeSelectedContactHelper.deleteSelectedContact}"
rendered="#{homeSelectedContactHelper.selectedContactManaged}"
styleClass="image-command-link">
<f:setPropertyActionListener value="#{null}"
target="#{homeContactsListHelper.contactsList}"/>
<h:graphicImage value="/img/delete.png"/>
<h:outputText value="#{messages['delete']}"/>
</a:commandLink>

Llama a el método homeSelectedContactHelper.deleteSelectedContact() que


establece en null a selectedContact y el selectedContactEditing (es una manera
diferente de hacer lo que hemos hecho para los botones de acción de otros utilizando
comoponentes f:setPropertyListener). A continuación, establece en null la propiedad de
contactos y reconstruye los cuadros contactList y contactDetail.

Aquí hay una captura de pantalla del cuadro de edición con la barra de herramientas:
Los atributos ajaxSingle y process
La propiedad ajaxSingle es muy útil para controlar el envío del formulario cuando
ajaxSingle se establece en true, el formulario no se envía sólo los datos de los
componentes Ajax.

Este atributo se encuentra disponible en todos los componentes de acción Ajax y podemos
usarlo para llamar a una acción de un botón, saltar la validación de formularios (como la
propiedad immediate de JSF), o para enviar el valor de entrada de sólo una en una forma sin
la validación y la la presentación de los otros.

El segundo caso de uso puede ser utilizado, por ejemplo, cuando necesitamos un menú de
entrada que cambia dinámicamente el valor de otras entradas, sin presentar el formulario
completo:

<h:form>
<!-- other input controls -->
<h:selectOneMenu id="country"
value="#{myBean.selectedCountry}">
<f:selectItems value="#{myBean.myCountries}">
<a:support event="onchange" ajaxSingle="true"
reRender="city" />
</h:selectOneMenu>
<h:selectOneMenu id="city"
value="#{myBean.selectedCity}">
<f:selectItems value="#{myBean.myCities}">
</h:selectOneMenu>
<!-- other input controls -->
</h:form>

En este ejemplo, cada vez que el usuario selecciona un país nuevo, el valor es envíado al bean
que vuelve a calcular la propiedad myCities para el nuevo país, después de que el menú de
la ciudad se vuelven a representar para mostrar las nuevas ciudades.

Todo lo que no se envía del formulario o se bloquean los cambios a causa de algún problema
de validación.

¿Qué pasa si usted desea enviar más de un valor, pero todavía no todo el formulario?
Podemos utilizar las regiones Ajax (que veremos en las siguientes secciones), o podemos usar
el atributo process. Este contiene una lista de componentes para el proceso, mientras que se
envía la acción Ajax:

<h:form>
<!-- other input controls -->
<h:inputText id="input1" ... />
<h:selectOneMenu id="country"
value="#{myBean.selectedCountry}">
<f:selectItems value="#{myBean.myCountries}">
<a:support event="onchange"
ajaxSingle="true"
process="input2, input3"
reRender="city" />
</h:selectOneMenu>
<h:inputText id="input2" ... />
<h:inputText id="input3" ... />
<h:selectOneMenu id="city"
value="#{myBean.selectedCity}">
<f:selectItems value="#{myBean.myCities}">
</h:selectOneMenu>
<!-- other input controls -->
</h:form>

En este ejemplo, también queríamos presentar los valores input2 y input3, junto con el
nuevo país, porque son útiles para la recuperación de la nueva lista de las ciudades,
simplemente estableciendo el atributo process con la lista de identificación y durante la
presentación, se procesarán. Así input1 no se enviará.

Además, para los componentes de acción, tales como botones, puede decidir qué enviar
utilizando los atributos ajaxSingle y process.

Formulario de presentación y procesamiento


Hablamos acerca de la forma "presentación" para simplificar el concepto y hacer las cosas más
comprensibles. En realidad, para cada petición, todos los formularios son enviados, pero sólo
los componentes seleccionados (con ajaxSingle y / o atributos process) serán
"transformados". Por "transformados" nos referimos a "pasar a través de" las fases de JSF
(decodificación, conversión, validación y el modelo de actualización).

Más Ajax
Por cada contacto, nos gustaría añadir más campos personalizables, así que vamos a utilizar la
entidad ContactField conectado a todas las instancias de Contact.

En primer lugar, vamos a crear un bean de soporte llamado


HomeSelectedContactOtherFieldsHelper dentro del paquete
book.richfaces.advcm.modules.main

Puede verse así:

@Name("homeSelectedContactOtherFieldsHelper")
@Scope(ScopeType.CONVERSATION)
public class HomeSelectedContactOtherFieldsHelper {
@In(create = true)
EntityManager entityManager;
@In(required = true)
Contact loggedUser;
@In
FacesMessages facesMessages;
@In(required = true)
HomeSelectedContactHelper homeSelectedContactHelper;
// my code
}

Algo notable que se resalta es el componente homeSelectedContactHelper, porque para


obtener la lista de los campos personalizados de la base de datos, necesitamos el propio
contacto. También se establece el atributo required en true, ya que este bean no puede
existir sin el contexto homeSelectedContactHelper.

Ahora, vamos a añadir la propiedad que contiene la lista de campos personalizados para los
contactos seleccionados:
private List<ContactField> contactFieldsList;
public List<ContactField> getContactFieldsList() {
if (contactFieldsList == null) {
// Getting the list of all the contact fields
String query = "from ContactField cf where cf.contact.id=:
idContactOwner order by cf.id";
contactFieldsList = (List<ContactField>)
entityManager.createQuery(query)
.setParameter("idContactOwner",
homeSelectedContactHelper.getSelectedContact()
.getId()).getResultList();
}
return contactFieldsList;
}
public void setContactFieldsList(List<ContactField>
contactFieldsList) {
this.contactFieldsList = contactFieldsList;
}

Como puede ver, es una característica normal inicializarlo usando el método de acceso. Esta
consulta la base de datos para recuperar la lista de campos personalizados para el contacto
seleccionado. Tenemos que poner en el bean de algún otro método útil para administrar el
campo personalizado (adición y eliminación de campo y de la base de datos), vamos a añadir
los métodos:

public void createNewContactFieldInstance() {


// Adding the new instance as last field
(for inserting a new field)
getContactFieldsList().add(new ContactField());
}
public void persistNewContactField(ContactField field) {
// Attaching the owner of the contact

field.setContact(homeSelectedContactHelper.getSelectedContact());
entityManager.persist(field);
}
public void deleteContactField(ContactField field) {
// If it is in the database, delete it
if (isContactFieldManaged(field)) {
entityManager.remove(field);
}
// Removing the field from the list
getContactFieldsList().remove(field);
}
public boolean isContactFieldManaged(ContactField field) {
return field != null && entityManager.contains(field);
}

El método createNewContactFieldInstance()sólo añadirá una nueva (aún persiste),


instancia vacía de la clase ContactField en la lista.

Después de que el usuario ha rellenado los valores, él / ella presiona un botón que llama al
método persistNewContactField () para guardar los nuevos datos en la base de datos.

Para eliminar, vamos a utilizar el método deleteContactField () y para determinar si una


instancia persiste en la base de datos o no, vamos a utilizar el método
isContactFieldManaged().
Ahora, vamos a abrir el archivo /view/main/contactView.xhtml y vamos a agregar el código que
mostrará los campos personalizados después de h:panelGrid:

<a:repeat value="#{homeSelectedContactOtherFieldsHelper.
contactFieldsList}" var="field">
<h:panelGrid columns="2" rowClasses="prop"
columnClasses="name,value">
<h:outputText value="#{field.type} (#{field.label}):"/>
<h:outputText value="#{field.value}"/>
</h:panelGrid>
</a:repeat>

Estamos utilizando un nuevo componente de iteración de datos RichFaces que nos permite
iterar sobre una colección y poner los datos que queremos (el componente rich:dataTable
crea una tabla con la lista de elementos).

En nuestro caso, el bloque h:panelGrid se repetirá para cada uno de los elementos de la
colección (para cada campo personalizado).

Ahora, vamos a abrir archivo /view/main/contactEdit.xhtml y agregue el código para


editar los campos personalizados en la lista:

<a:region>
<a:outputPanel id="otherFieldsList">
<a:repeat value="#{homeSelectedContactOtherFieldsHelper.
contactFieldsList}"
var="field">
<h:panelGrid columns="3" rowClasses="prop"
columnClasses="name,value,validatormsg">
<h:panelGroup>
<h:inputText id="scOtherFieldType"
value="#{field.type}"
required="true" size="5">
<a:support event="onblur"
ajaxSingle="true"/>
</h:inputText>
<h:outputText value=" ("/>
<h:inputText id="scOtherFieldLabel"
value="#{field.label}"
size="5">
<a:support event="onblur"
ajaxSingle="true"/>
</h:inputText>
<h:outputText value=")"/><br/>
<rich:message for="scOtherFieldType"
styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
</h:panelGroup>
<h:panelGroup>
<h:inputText id="scOtherFieldValue"
value="#{field.value}"
required="true">
<a:support event="onblur"
ajaxSingle="true"/>
</h:inputText><br/>
<rich:message for="scOtherFieldValue"
styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
</h:panelGroup>
<h:panelGroup>
<a:commandButton image="/img/add.png"
reRender="otherFieldsList"
action="#{homeSelectedContactOtherFieldsHelper.
persistNewContactField(field)}"
rendered="#{!homeSelectedContactOtherFieldsHelper.
isContactFieldManaged(field)}">
</a:commandButton>
<a:commandButton image="/img/remove.png"
reRender="otherFieldsList" ajaxSingle="true"
action="#{homeSelectedContactOtherFieldsHelper.
deleteContactField(field)}">
</a:commandButton>
</h:panelGroup>
</h:panelGrid>
</a:repeat>
<a:commandLink reRender="otherFieldsList"
ajaxSingle="true"
action="#{homeSelectedContactOtherFieldsHelper.
createNewContactFieldInstance}"
rendered="#{homeSelectedContactHelper.
selectedContactManaged}"
styleClass="image-command-link">
<h:graphicImage value="/img/add.png"/>
<h:outputText value="#{messages['addNewField']}"/>
</a:commandLink>
</a:outputPanel>
</a:region>

El código es muy similar a la del cuadro de vista, excepto por los botones de acción (para
añadir una nueva instancia, persistencia, guardar o eliminar) y, por la presencia de la etiqueta
que rodea a:region (resaltado). Esto es muy importante para asegurarse de que la forma
funciona correctamente, vamos a ver por qué en la siguiente sección.

También observe que todos los componentes de entrada tiene la etiqueta a:support como un
hijo que va a actualizar el bean editando el valor en el evento onblur (lo que significa que cada
vez que cambia el foco a otro componente, el valor de la última es enviado). Así que, si elimina
o añade un campo, ahora se perderán los valores editados en otros campos. También se utiliza
para la validación del Ajax, y el usuario es informado de que el valor no es válido cuando se
mueve el cursor a otra entrada.

Aquí hay una captura de pantalla con la nueva característica en el cuadro de edición:
Usando a:support para la validación de Ajax

Si desea utilizar la a:support únicamente para fines de validación, recuerde poner el atributo
bypassUpdates en true, por lo que el proceso sería más rápido que el modelo de
actualización de JSF y las fases de invocación de la aplicación no serán invocadas.

Contenedores Ajax
Durante el desarrollo de una aplicación web con RichFaces, es muy útil saber cómo utilizar los
contenedores Ajax (como el comoponente a:region) a fin de optimizar las peticiones Ajax.

En esta sección, vamos a discutir sobre el componente a:region.

Es un componente muy importante del marco de trabajo, ya que puede definir las áreas Ajax
para limitar la parte del árbol de componentes que serán procesados en una petición Ajax.

Las regiones se pueden anidar en una petición Ajax y el más cercano será utilizado.

Al establecer a true el atributo a:region llamado regionRenderOnly, puede utilizar este


componente para limitar la actualización de los elementos, de este modo, de hecho, sólo los
componentes dentro de la región se pueden actualizar.

Otro atributo importante es selfRendered, estableciendo ésta a true indica el marco para
crear la respuesta basándose en el árbol de componentes sin hacer referencia al código de la
página, es más rápido, pero todos los elementos transitorios que no se guardan en el árbol
(como f:verbatim ó código HTML escrita directamente sin necesidad de utilizar
componentes JSF) se perderán la primera vez de refrescado, por lo que no se pueden utilizar
en este caso.

En resumen, es muy útil para controlar el proceso de representación y optimizar, con el fin de
limitar los elementos de un formulario para enviar durante una petición Ajax sin problemas de
validación, para mostrar los diferentes indicadores de la condición Ajax.

Ejemplo del uso de a:region:


<h:form>
<a:region> <h:inputText id="it1" value="#{aBean.text1}"> <a:support
event="onkeyup" reRender="text1" /> </h:inputText> <h:inputText
id="it2" value="#{aBean.text2}" /> </a:region>
<h:inputText id="it3" value="#{aBean.text3}" />
<a:commandButton
action="#{aBean.saveTexts}" reRender="text1,text2" />
</h:form>
<h:outputText id="text1" value="#{aBean.text1}" />
<h:outputText id="text2" value="#{aBean.text2}" />

En este ejemplo, mientras el usuario está escribiendo en el valor del text1 de inputText,
a:support envía una petición Ajax que contiene sólo los valores it1 y it2 de inputText.

En este caso, de hecho, a:region limíta los componentes enviados por cada petición Ajax se
originó en el interior de la región. Por lo tanto, la petición Ajax sólo se actualizará
aBean.text1 y aBean.text2.

Envolver sólo un componente dentro de una región del Ajax es el equivalente de usar la
propiedad ajaxSingle establecida a true.

Si el usuario hace clic en el control a:commandButton aBean.text1, los valores


aBean.text2 y aBean.text3 serán actualizados por la petición Ajax.

Volviendo a nuestra aplicación, como todos los campos personalizados dentro del mismo
componente de formulario, nos rodean cada uno de ellos con la etiqueta a:region. De esta
manera, el campo simplemente se presenta, independientemente de los demás.

Por ejemplo, sin utilizar a:region, si el usuario se vacía el valor de entrada del nombre y
luego trata de insertar un campo personalizado nuevo, el proceso fallará porque el nombre de
entrada no está validado. Si usamos el componente a:region, el campo de nombre no será
procesada y un nuevo campo será insertado.

Ahora que sabemos cómo utilizar la etiqueta a:region, podemos combinarlo con
ajaxSingle y process para decidir qué enviar en cada solicitud y para optimizar mejor las
interacciones Ajax en la aplicación.

Iteración de datos usando RichFaces


Todos los componentes de datos de iteración RichFaces comparten el mismo modo de trabajo
del estándar h:dataTable. Así que, una vez que sabe cómo usarlo, será capaz de utilizar
todos ellos sin ningún problema.

Echemos un vistazo de nuevo en una versión simple de los rich:dataTable que hemos
utilizado para la lista de contactos:

<rich:dataTable
value="#{homeContactsListHelper.contactsList}"
var="contact">
<rich:column>
<h:outputText value="#{contact.name}"/>
</rich:column>
<rich:column>
<h:outputText value="#{contact.surname}"/>
</rich:column>
</rich:dataTable>

El resultado de este código es simplemente previsible una tabla con dos columnas, una para el
nombre y el otro para el apellido.

Ahora, supongamos que no quiero que el formato de tabla, sino una lista de contactos, lo único
que tienes que hacer es utilizar el componente rich:dataList la misma forma que
rich:dataTable:

<rich:dataList value="#{homeContactsListHelper.contactsList}"
var="contact">
<h:outputText value="#{contact.name} #{contact.surname}"/>
</rich:dataList>

Para cada elemento, se hará una lista de contactos de la siguiente manera:

Exactamente el mismo mecanismo que le permite utilizar componentes


rich:dataOrderingList y rich:dataDefinitionList.

Una mención especial para los rich:dataGrid que tiene algunas diferencias:

<rich:dataGrid value="#{homeContactsListHelper.contactsList}"
var="contact"
columns="3">
<h:outputText
value="#{contact.name} #{contact.surname}"/>
</rich:dataGrid>

Como puede ver, hemos tenido que poner el atributo columns para saber cuándo termina la
fila y empieza una nueva. Aquí está el resultado:

Usted puede colocar cualquier componente que desee en su interior:

<rich:dataGrid value="#{homeContactsListHelper.contactsList}"
var="contact"
columns="3">
<rich:panel>
<f:facet name="header">
<h:outputText value="Contact" />
</f:facet>
<h:outputText
value="#{contact.name} #{contact.surname}"/>
</rich:panel>
</rich:dataGrid>

Y el resultado es:
Paginación de datos con los componentes de la iteración de datos
En cuanto a h:dataTable (y rich:dataTable), puede adjuntar datascroller a cada
componente de iteración de datos de la misma forma que lo hizo con su DataTable:

<h:form>
<rich:dataList id="contactsList"
value="#{homeContactsListHelper.contactsList}"
var="contact"
rows="3">
<h:outputText value="#{contact.name} #{contact.surname}"/>
</rich:dataList>
<rich:datascroller for="contactsList" />
</h:form>

Sólo recuerde colocar datascroller denttro de form, el atributo set, y establecer el atributo
de rows para uno de los componentes de iteración de datos.

El resultado es el siguiente:

Además, en este caso, existe una pequeña excepción para el componente rich:dataGrid,
ya que no tiene el atributo rows, pero el correspondiente es elements (se puede averiguar
por qué). Por lo tanto, nuestro ejemplo será:

<h:form>
<rich:dataGrid id="contactsGrid"
value="#{homeContactsListHelper.contactsList}"
var="contact"
columns="3"
elements="3">
<rich:panel>
<f:facet name="header">
<h:outputText value="Contact" />
</f:facet>
<h:outputText
value="#{contact.name} #{contact.surname}"/>
</rich:panel>
</rich:dataGrid>
<rich:datascroller for="contactsGrid" />
</h:form>
Y el resultado sería el siguiente:

Administración de direcciones
Otra pieza que nos gustaría hacer es la administración de direcciones, utilizando la entidad
ContactAddress. Este es el mismo mecanismo de trabajo que en el campo personalizado,
por lo que se deja como ejercicio para el lector.

Sin embargo, usted puede encontrar la función desarrollada en el código fuente de la


aplicación.

Algunas capturas de pantalla


Aquí, presentamos algunas capturas de pantalla de la aplicación con todas las características
que hemos visto hasta ahora.

Podemos ver la lista de contactos con un contacto seleccionado:

El formulario de edición de contacto (se nota en los botones para agregar / eliminar más
campos y direcciones) aparece como sigue:
Y finalmente tu puedes ver y agregar el formulario de contactos:

Resumen
En este capítulo, hemos desarrollado la característica principal de nuestra aplicación de
administración de contactos. Hemos aprendido sobre la interacción del Ajax y los contenedores
y sobre los nuevos componentes que Ajax RichFaces ofrece.

En el próximo capítulo, vamos a desarrollar nuevas funciones para el Administrador de


contactos Avanzado y el descubriremos los nuevos componentes y patrones RichFaces.

You might also like