Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Terminando la aplicación
Casi hemos terminado nuestra aplicación. En este capítulo, vamos a desarrollar una
característica muy útil que nos servirá para aprender a utilizar otros componentes RichFaces en
una aplicación real.
Un editor rico
Vamos a utilizar el componente de edición rich:editor del marco RichFaces. Se crea un
editor WYSIWYG basado en el código JavaScript de TinyMCE (http://tinymce.moxiecode.com).
Este componente es muy fácil de utilizar, pero al mismo tiempo, se puede personalizar y
extender utilizando los plugins de TinyMCE.
Con el fin de conectar el editor de la propiedad note de la entidad Contact, vamos a abrir el
archivo /view/main/contactEdit.xhtml e insertaremos el siguiente código, entre las
etiquetas </a:region> y </rich:graphValidator>, antes de rich:toolBar.
<rich:spacer height="10"/>
<rich:separator width="320px"/>
<rich:spacer height="15"/>
<h:outputText value="#{messages['note']}"/>
<rich:editor
value="#{homeSelectedContactHelper.selectedContact.note}"
width="320" height="150"/>
La primera parte es simplemente para crear un separador y una etiqueta, la parte más
importante es uno de los más destacados, hemos vinculado rich:editor a la propiedad
note, y definimos dos atributos (width y height) para controlar la dimensión del área de
edición.
<rich:editor
value="#{homeSelectedContactHelper.selectedContact.note}"
theme="advanced" width="320" height="150"/>
El resultado es:
<rich:editor
value="#{homeSelectedContactHelper.selectedContact.note}
width="320" height="150" theme="advanced" plugins="paste">
<f:param name="theme_advanced_buttons1" value="cut,copy,paste,
pasteword,|,bold,italic,underline"/>
<f:param name="theme_advanced_toolbar_location" value="top"/>
<f:param name="theme_advanced_toolbar_align" value="left"/>
</rich:editor>
Este es el resultado:
Observe que las barras de herramientas se encuentran ahora en la parte superior y la primera
barra tiene un botón diferente como se decidió (botones paste y pasteword provienen del
plugin paste que añadimos).
Usando la etiqueta f:param, puede configurar todas las opciones de TinyMCE (lea la
documentación de TinyMCE para más información, la opción va dentro del atributo name y el
valor de la opción va en el atributo value.
Con el fin de hacer eso, vamos a aplicar la característica de grupo -básicamente-, puede
agregar tantos grupos como desee y cada contacto puede quedarse en uno o más grupos.
Cada grupo tiene un color y una descripción. Vamos a ver cómo implementarlo.
Por otra parte, la primera columna de la tabla contiene una pequeña flecha que será visible
cuando la propiedad groupFilter es nula (de modo que todos los contactos se muestran).
Ahora vamos a cambiar el método getContactList() para hacer uso de la nueva propiedad:
Además, queremos mostrar que la tabla de lista de contactos es filtrada cambiando el texto de
cabecera de la tabla y el color (de acuerdo con el actual grupo de colores filtrados).
<f:facet name="header">
<rich:columnGroup>
<rich:column colspan="3">
<h:outputText value="#{messages['allContacts']}"
rendered="#{homeContactsListHelper.groupFilter==null}"/>
<h:outputText value="#{messages['contactsInGroup']}:
#{homeContactsListHelper.groupFilter.name}"
style="color: #{homeContactsListHelper.
groupFilter.color};margin-left: 5px;"
rendered="#{homeContactsListHelper.
groupFilter!=null}"/>
</rich:column>
<rich:column breakBefore="true">
<h:outputText value="#{messages['name']}"/>
</rich:column>
<rich:column>
<h:outputText value="#{messages['surname']}"/>
</rich:column>
<rich:column>
<rich:spacer/>
</rich:column>
</rich:columnGroup>
</f:facet>
Tenemos que crear el bean que administra la lista de grupos, el grupo de adición y eliminación,
vamos a crear una nueva clase llamada GroupsListHelper dentro nuevo paquete
book.richfaces.advcm.modules.main.groups de la siguiente manera:
@Name("groupsListHelper")
@Scope(ScopeType.CONVERSATION)
public class GroupsListHelper {
@In(create = true)
EntityManager entityManager;
@In(required = true)
Contact loggedUser;
@In
FacesMessages facesMessages;
}
Vamos a llenar los componentes vacíos de Seam con la lógica a la lista de grupos:
Hemos visto este tipo de código en otra parte del libro que hemos desarrollado -no es sólo una
propiedad con un accesor y un modificador, sino que en el interior del captador, está el código
que carga la lista utilizando el código estándar JPA.
Inserte el siguiente código justo antes de la etiqueta de cierre </table> del archivo:
Uso de la etiqueta a:repeat (que, como hemos visto en otros capítulos, trabaja como los
componentes de iteración de los otros datos), podemos insertar una nueva fila en la tabla para
cada grupo.
Esta vez la flecha de la primera columna se muestra sólo si el grupo en esa fila es la que se
muestra en ese momento.
También estamos utilizando rich:spacer con la propiedad de fondo CSS con el valor del
grupo de colores, a fin de mostrar un recuadro de color para cada grupo pequeño, veremos
cómo configurar el color más adelante en este capítulo.
Después de que el comando de enlace que activa el grupo de filtrado y vuelve a cargar la lista
de contactos para mostrar la nueva tabla de filtrado de contactos, podemos ver una nueva
etiqueta llamada rich:toolTip esta etiqueta es muy útil para mostrar una herramienta que
muestra una breve descripción del grupo, cuando nos posicionamos sobre la celda de la tabla
que contiene el nombre del grupo.
<a:outputPanel>
<rich:toolTip value="This is the text of my tooltip!" />
</a:outputPanel>
<a:outputPanel id="myPanel">
<rich:toolTip value="This is the text of my tooltip!"
for="myPanel"/>
</a:outputPanel>
Muy simple.
Nota que cuando se utiliza el atributo de la etiqueta rich:toolTip no tiene que ser anidada
dentro del componente (por ejemplo un a:outputPanel en nuestro caso).
<a:outputPanel>
<rich:toolTip mode="ajax" value="#{myBean.myProperty}">
<f:facet name="defaultContent">
<h:outputText value="Loading... " />
</f:facet>
</rich:tooltip>
</a:outputPanel>
Este código mostrará la herramienta ventana contextual y dentro el texto Loading…, hasta
que el contenido de la propiedad de la envolvente (myProperty) se cargue utilizando Ajax, y
esté listo para ser mostrado.
Vamos a hacer esto dentro de la tabla del grupo, por lo que el grupo que está editando (o
agregando) se muestra como un formulario con entradas para agregar o modificar y no como
un enlace.
Para entender mejor esto, esta es una captura de pantalla de la caracteristica deseada:
Como puede ver, el formulario de edición se encuentra dentro de la tabla, en el lugar de edicion
del grupo (para añadir el formulario será en la último renglón de la tabla).
En primer lugar, vamos a agregar la propiedad groupEditing que contiene el grupo que se
está editando:
No hay nada nuevo aquí, sólo es una propiedad con un accesor y un modificador.
Ahora, hay que asegurarse de que al añadir y editar botones llenemos esta propiedad cuando
se haga clic en él.
Para la función de edición, sólo puedo añadir la siguiente columna a la tabla del grupo dentro
del archivo contactsGroups.xhtml:
Para el botón Agregar, tenemos que utilizar un método que crea una nueva instancia
ContactGroup y lo inserte en la lista.
getGroups().add(newGroup);
setGroupEditing(newGroup);
}
Ahora, podemos agregar una barra de herramientas con el botón Agregar grupo justo debajo
de la tabla de los grupos, vamos a cambiar al archivo contactsGroups.xhtml y agregar
este código después de la etiqueta de cierre </rich:panel>:
<rich:toolBar>
<rich:toolBarGroup>
<a:commandButton
image="/img/addgroup.png"
ajaxSingle="true"
reRender="contactsGroups"
action="#{groupsListHelper.addGroup}"/>
</rich:toolBarGroup>
</rich:toolBar>
El botón que hemos añadido llama al método addGroup() y hace que aparezca la tabla de
grupos de contacto.
<td>
<a:outputPanel
rendered="#{groupsListHelper.groupEditing.id!=group.id}">
<rich:spacer width="10" height="10"
style="background-color: #{group.color}"/>
<a:commandLink value="#{group.name}"
reRender="contactsList, contactsGroups">
<f:setPropertyActionListener value="#{group}"
target="#{homeContactsListHelper.groupFilter}"/>
<f:setPropertyActionListener value="#{null}"
target="#{homeContactsListHelper.contactsList}"/>
</a:commandLink>
<rich:toolTip value="#{group.description}"/>
</a:outputPanel>
</td>
Al hacer esto, se cierra el código que muestra únicamente el grupo recargado y no está en el
modo de edición.
Después de este panel, se tendrá que insertar el panel que contiene el código para los grupos
en el modo de edición, de modo que el usuario podrá editar las propiedades de un grupo que
está en el modo de edición.
<rich:panel rendered="#{groupsListHelper.groupEditing.id==group.id}">
<f:facet name="header">
<h:panelGroup>
<h:outputText value="#{messages['editGroup']}"
rendered="#{groupsListHelper.groupEditing.id!=null}"/>
<h:outputText value="#{messages['addGroup']}"
rendered="#{groupsListHelper.groupEditing.id==null}"/>
</h:panelGroup>
</f:facet>
<a:region>
<h:panelGrid columns="1">
<h:inputText id="newGroupName"
value="#{groupsListHelper.groupEditing.name}"
required="true">
<rich:beanValidator/>
</h:inputText >
<rich:message for="newGroupName"
styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
<rich:colorPicker
value="#{groupsListHelper.groupEditing.color}"/>
<rich:editor
value="#{groupsListHelper.groupEditing.description}"/>
<a:outputPanel>
<a:commandButton value="#{messages['save']}"
action="#{groupsListHelper.saveGroupEditing}"
reRender="contactsGroups"/>
<a:commandButton
value="#{messages['cancelEditing']}"
ajaxSingle="true"
reRender="contactsGroups">
<f:setPropertyActionListener
value="#{null}"
target="#{groupsListHelper.groupEditing}"/>
</a:commandButton>
</a:outputPanel>
</h:panelGrid>
</a:region>
</rich:panel>
Este componente permite al usuario elegir un color de forma visual y es muy útil y fácil de usar.
Puede utilizar CSS para personalizarlo (como ocurre con todos los componentes de
RichFaces), y usar dos facets para sobreescribir las estándard establecimiento el facet icon,
puede personalizar el icono que abre el panel, mientras que con los facets arrows, puede
cambiar el color de las flechas de selección.
Si nos fijamos en otro componente, la lógica de trabajo de esta formulario es limpia, cuando el
grupo está en el modo de edición, se muestra este formulario con la propiedad ContactGroup
vinculado al componente de entrada, al final del formulario, tenemos dos botones, uno para
confirmar y guardar el grupo editado en la base de datos y la otra para cancelar la edición (que
sólo establece la propiedad groupEditing a nulo y recarga la lista de grupos).
Para guardar el grupo en la base de datos hay que agregar otro método a la clase
GroupsListHelper:
En la primera parte del método, se comprueba si el grupo que se eliminará está seleccionado:
Si es así, quitamos la selección y luego lo eliminamos de la base de datos. Tenemos que
obtener y establecer la propiedad groupFilter que está dentro del componente
homeContactsListHelper, por eso se inyecta en una propiedad local, utilizando el código
fuera del método.
Ahora tenemos que crear el botón en XHTML y vincularlo con este método. Vamos a abrir el
archivo contactsGroups.xhtml y agregamos otra columna después para su edición:
Hemos terminado nuestras funciones de administración de grupos, ahora tenemos que hacer
posible que el usuario pueda insertar los contactos en los grupos.
<rich:column width="45%"
sortBy="#{contact.name}"
filterBy="#{contact.name}">
<h:outputText value="#{contact.name}"/>
<rich:dragSupport dragType="contact"
dragIndicator="contactDragIndicator"
dragValue="#{contact}">
<rich:dndParam type="drag" name="label"
value="#{contact.name} #{contact.surname}"/>
</rich:dragSupport>
</rich:column>
<rich:column width="45%"
sortBy="#{contact.surname}"
filterBy="#{contact.surname}">
<h:outputText value="#{contact.surname}"/>
<rich:dragSupport dragType="contact"
dragIndicator="contactDragIndicator"
dragValue="#{contact}">
<rich:dndParam type="drag" name="label"
value="#{contact.name} #{contact.surname}"/>
</rich:dragSupport>
</rich:column>
También se define un parámetro llamado arrastre label con el nombre y apellido del contacto.
Vamos a utilizarlo en el indicador de arrastre.
Un indicador de arrastre es un panel HTML que sigue el ratón mientras se arrastra, no tenemos
definido nuestro indicador personal. Sin embargo, en este caso, queremos mostrar alguna
información sobre el elemento arrastrado (el nombre y apellido) dentro del panel.
Es sólo un rectángulo de puntos, que se convierte en verde cuando el tema es arrastrado por
un panel drop, que la acepta.
El uso básico de la dragIndicator es muy simple, sólo basta con colocar el siguiente código
al final de la página, antes del cierre </ ui:composición> tag:
Podemos personalizar cada parte del indicador de arrastre usando CSS y facets.
Ahora tenemos que crear una area drop de cada grupo que acepta los elementos arrastrados
del tipo de contacto.
Vamos a abrir el archivo contactsGroups.xhtml y añade este código dentro del grupo
commandLink:
<a:commandLink value="#{group.name}"
style="color: #{group.color};margin-left: 5px;"
reRender="contactsList, contactsGroups">
<f:setPropertyActionListener value="#{group}"
target="#{homeContactsListHelper.groupFilter}"/>
<f:setPropertyActionListener value="#{null}"
target="#{homeContactsListHelper.contactsList}"/>
<rich:dropSupport acceptedTypes="contact"
dropListener="#{groupsListHelper.
processDropAddContactToGroup}"
dropValue="#{group}"/>
</a:commandLink>
El código en negrita añade el soporte drop a todos los enlaces del grupo. Se acepta sólo el tipo
de contact arrastrando elementos y tiene un valor desplegable que contiene la instancia de
group actual.
El listener definido para el evento soltar será llamado cada vez que un elemento se ha soltado,
vamos a hacerlo en el interior de la clase GroupsListHelper:
En este método, se obtiene los valores dragValue y dropValue, y los utilizan para comprobar si
el contacto está dentro del grupo y si desea o no insertar.
Esta es una manera muy simple y eficaz para agregar arrastrar y colocar su solicitud.
El tipo de arrastrar/soltar es muy útil para definir diferentes lógicas de arrastrar/soltar en función
del tipo de elemento arrastrado.
Esta área debe ser sólo aparece cuando la lista de contactos está mostrando el contenido de
un grupo por lo tanto, se muestra cuando la propiedad
homeContactsListHelper.groupFilter no es nulo.
Dejamos como ejercicio para el lector. Puede encontrar el código fuente completo descargando
el código de la aplicación.
Adjuntar archivos
Nos gustaría asociar uno o más archivos a cada contacto. Por ejemplo, podemos adjuntar una
imagen, o el CV, u otra información útil sobre el contacto.
Creación de la asistente
Empecemos por el asistente para cargar el archivo, se trata de un sencillo asistente de una
página que permite al usuario subir y luego revisar los archivos, finalmente añadir una nota a
cada uno.
Ahora tenemos que introducir (en lugar del comentario <!-- my code --> ) el formulario
con la carga de archivo de RichFaces:
<h:form>
<h:outputText value="#{messages['selectFilesToUpload']}" />
<rich:fileUpload acceptedTypes="gif,jpg,png,pdf,doc,xls"
allowFlash="auto" autoclear="false"
maxFilesQuantity="10" immediateUpload="true"
fileUploadListener="#{filesUploadHelper.listener}">
<a:support event="onuploadcomplete" reRender="nextBtn" />
</rich:fileUpload>
</h:form>
La forma de trabajo es muy intuitivo para cada archivo suba, un método de escucha se ejecuta,
por lo que la aplicación puede manejar el archivo cargado.
Los otros atributos que nos hemos fijado son fáciles de entender, pero una mención especial va
para el atributo allowFlash, que permite la habilitación de un panel de componentes de
carga Flash. Si se activa el plugin de Flash, permitirá al usuario elegir más de un archivo a
cargar a la vez.
Para cambiar el valor del parámetro, hay que abrir el archivo web.xml y añadir este código:
<init-param>
<param-name>createTempFiles</param-name>
<param-value>false</param-value>
</init-param>
Antes de crear el bean, tenemos que definir la ruta donde los archivos subidos se guardarán, y
podemos usar el componente uiOptions para eso. Sólo tienes que abrir la clase UIOption y
agregar la siguiente propiedad:
Ahora tenemos que configurar en el archivo components.xml. Vamos a abrirlo y añadir una
propiedad a la inicialización de componentes UIOption:
<property name="fileSavePath">/my/file/path/</property>
Por razones de seguridad, es altamente recomendable poner la ruta del archivo fuera de la
aplicación, veremos cómo habilitar el acceso a través de la aplicación más adelante. Ahora,
necesitamos el componente de Seam que administra el proceso de carga de archivos
(filesUploadHelper). Vamos a crear una nueva clase llamada FilesUploadHelper
dentro del paquete book.richfaces.advcm.modules.main.files:
@Name("filesUploadHelper")
@Scope(ScopeType.CONVERSATION)
public class FilesUploadHelper { @In(create = true)
EntityManager entityManager;
@In(required = true)
HomeSelectedContactHelper homeSelectedContactHelper;
@In
UIOptions uiOptions;
}
Como puede ver, este método obtiene la instancia UploadItem del archivo subido, crea la
asociación con el contacto seleccionado así como la persistencia en la base de datos. Después
de que copia el archivo temporal a nuestra posición preferida, el método CopyFile siguiente
se utiliza para esto:
Este es un bean casi general que se puede utilizar en su aplicación con sólo cambiar el código
de la base de datos.
Este botón permite al usuario navegar a la página siguiente utilizando las reglas de navegación
de JSF desde donde next viene. Vamos a abrir el archivo faces-config.xml y agregar el
código siguiente después de la etiqueta de cierre </application>:
<navigation-rule>
<from-view-id>/main/uploadFiles/wizardFirstStepUploadFiles.xhtml
</from-view-id>
<navigation-case>
<from-outcome>next</from-outcome>
<to-view-id>/main/uploadFiles/wizardSecondStepUploadFiles.xhtml
</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>
/main/uploadFiles/wizardSecondStepUploadFiles.xhtml
</from-view-id>
<navigation-case>
<from-outcome>previous</from-outcome>
<to-view-id>
/main/uploadFiles/wizardFirstStepUploadFiles.xhtml
</to-view-id>
</navigation-case>
</navigation-rule>
Esta es un JSF estándar que permite la navegación a la página del segundo paso en el caso
de los resultados que viene, y volver a la primera en caso de que el resultado anterior.
Ahora vamos a crear un archivo vacío (usando el mostrado previamente plantilla) vuelva a
llamar wizardSecondStepUploadFiles.xhtml y agregue el siguiente código:
<h:form>
<ui:include src="showCurrentContactFiles.xhtml">
<ui:param name="edit" value="true"/>
<ui:param name="columns" value="2"/>
</ui:include>
<a:commandLink ajaxSingle="true" action="previous"
reRender="uploadImagesWizard" style="float:left;"
styleClass="image-command-link">
<h:graphicImage value="/img/previous.png"/>
<h:outputText value="#{messages['previous']}"/>
</a:commandLink>
</h:form>
Se puede notar que hay un archivo (utilizando etiqueta la de inclusión Facelets ui:include)
con dos parámetros que se pasan, eso es porque vamos a volver a utilizar el código para
mostrar la lista de los archivos para otra característica (se verá más adelante).
Como se puede ver (el código en negrita), este panel se muestra sólo cuando el parámetro de
Facelets editar se establece en true. Más tarde agregará otro panel para administrar el caso
de que modificar es el valor false.
Vamos a crear un nuevo archivo (con la plantilla vacía que hemos visto) dentro de la carpeta
/view/main/uploadFiles/, el cual llamaremos uploadFilesModalPanel.xhtml, y
escribiremos el código siguiente dentro de la etiqueta ui:component:
<rich:modalPanel id="uploadFilesMP"
minHeight="300"
minWidth="350"
autosized="true"
moveable="true"
resizeable="false">
<f:facet name="header">
<h:outputText value="#{messages['uploadNewFiles']}"/>
</f:facet>
<!-- my code -->
</rich:modalPanel>
<rich:toolBarGroup location="right">
<a:commandLink
onclick="#{rich:component('uploadFilesMP')}.show();"
styleClass="image-command-link">
<h:graphicImage value="/img/upload.png"/>
<h:outputText value="#{messages['uploadFiles']}"/>
</a:commandLink>
</rich:toolBarGroup>
La línea resaltada es la que hace el truco para cerrar el panel (por ejemplo, mediante un botón
en su interior). Podemos utilizar el mismo código, pero llame el JavaScript hide() en lugar del
método show(), muy sencillo realmente.
Recuerde que para que funcione, tenemos que incluir el archivo en nuestra página, así que
vamos a abrir el archivo home.xhtml y agregue el código siguiente al final, después de la
etiqueta de cierre </h:panelGrid>:
Hagamos un ejemplo de uso mediante una llamada al show() la función de nuestro panel
modal utilizando rich:componentControl:
<a:commandLink styleClass="image-command-link">
<h:graphicImage value="/img/upload.png"/>
<h:outputText value="#{messages['uploadFiles']}"/>
<rich:componentControl for="uploadFilesMP"
event="onclick" operation="show"/>
</a:commandLink>
<a:outputPanel id="uploadFilesWizard">
<a:include ajaxRendered="true"
viewId="/main/uploadFiles/wizardFirstStepUploadFiles.xhtml"/>
</a:outputPanel>
Ahora, vamos a agregar el botón de cierre en el primer paso del asistente (al archivo
/view/main/uploadFiles/wizardFirstStepUploadFiles.xhtml):
<a:commandLink onclick="#{rich:component('uploadFilesMP')}.hide();"
style="float:right;" styleClass="image-command-link">
<h:graphicImage value="/img/close.png" />
<h:outputText value="#{messages['close']}" />
</a:commandLink>
Esto es sólo el código estándar que hemos visto para el cierre de un panel de modal (observa
la llamada al método hide() ).
En la segunda etapa, también queremos salvar los cambios antes de cerrar el panel, por lo que
hay que agregar el código ( al archivo
/view/main/uploadFiles/wizardSecondStepUploadFiles.xhtml ) es ligeramente diferente:
<a:commandLink
action="#{filesListHelper.updateList}"
oncomplete="#{rich:component('uploadFilesMP')}.hide();"
style="float:right;" styleClass="image-command-link">
<h:graphicImage value="/img/close.png"/>
<h:outputText value="#{messages['finish']}"/>
</a:commandLink>
Aquí están las dos capturas de pantalla de los pasos dentro del panel modal:
Terminando la función de carga de archivos
La última funcionalidad que vamos a agregar es el panel de archivos para mostrar (o editar si
está en el modo de edición) los archivos asociados a un contacto.
Para hacer eso, vamos a utilizar lo que hemos visto hasta ahora, así que vamos a empezar a
crear un nuevo archivo XHTML con un panel llamado showFilesModalPanel.xhtml modal
que contiene el código siguiente:
<ui:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<h:form>
<a:outputPanel id="showFilesWizard">
<ui:include src="showCurrentContactFiles.xhtml">
<ui:param name="edit"
value="#{homeSelectedContactHelper.
selectedContactEditing}"/>
<ui:param name="columns" value="3"/>
</ui:include>
<br/>
<h:panelGroup style="float:right;">
<a:commandLink action="#{filesListHelper.updateList}"
oncomplete="#{rich:component('showFilesMP')}.hide();"
rendered="#{homeSelectedContactHelper.
selectedContactEditing}"
styleClass="image-command-link">
<h:graphicImage value="/img/files.png"/>
<h:outputText value="#{messages['save']}"/>
</a:commandLink>
<rich:spacer width="5"/>
<a:commandLink action="previous"
onclick="#{rich:component('showFilesMP')}.hide();"
styleClass="image-command-link">
<h:graphicImage value="/img/close.png"/>
<h:outputText value="#{messages['close']}"/>
</a:commandLink>
</h:panelGroup>
</a:outputPanel>
</h:form>
</rich:modalPanel>
</ui:component>
Aquí, hemos creado un grupo especial de paneles modales que incluye el archivo
showCurrentContactFiles.xhtml ( que hemos creado) para ver los archivos en las
diferentes rejillas. Esta vez, el parámetro Facelets edit depende de la propiedad
homeSelectedContactHelper.selectedContactEditing (como se ve en el código en
negrita) así, se establece en true en el modo edit, pero false en caso contrario.
Después de ui:include, tenemos dos botones, uno es el botón Save (sólo se muestra
cuando se está en el modo edit ) y el otro es el botón Close.
Es hora de implementar la recarga del panel cuando no se está en el modo edit dentro del
archivo showCurrentContactFiles.xhtml, vamos a abrirlo de nuevo y agregaremos el
siguiente código justo después de la etiqueta <rich:dataGrid ..>:
<a:outputPanel layout="block"
rendered="#{edit==false}"
style="text-align: center;">
<h:outputText value="#{file.fileName}"
style="font-weight: bold;"/>
<br/><br/>
<h:outputText value="#{file.description}" escape="false"/>
<br/><br/>
<s:link action="#{fileDownloadHelper.download}"
styleClass="image-command-link">
<f:param name="cid" value="#{file.id}"/>
<h:graphicImage value="/img/download.png"/>
<h:outputText value="#{messages['download']}"/>
</s:link>
</a:outputPanel>
ServletOutputStream out;
out = response.getOutputStream();
out.write(fileContent);
out.flush();
facesContext.responseComplete();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Es útil para la descarga de archivos pequeños (menor que Integer.MAX_VALUE), para los
grandes es mejor utilizar un servlet o un recurso Seam a la medida.
El último paso es añadir el código para abrir el panel de modos de transporte, tanto en la vista y
en modo de edición.
<rich:toolBarGroup location="right">
<a:commandLink
oncomplete="#{rich:component('showFilesMP')}.show();"
ajaxSingle="true" reRender="showFilesPanel"
styleClass="image-command-link">
<f:setPropertyActionListener value="#{null}"
target="#{filesListHelper.files}"/>
<h:graphicImage value="/img/files.png"/>
<h:outputText value="#{messages['files']}"/>
</a:commandLink>
</rich:toolBarGroup>
commandLink obligará a que se vuelva a leer la lista de archivos (mediante el establecimiento
a null) y volverá a reconstruir a showFilesPanel para sincronizar los cambios, después de
que el panel modal será abierto por el código JavaScript onComplete.
Aquí hay una captura de pantalla de la barra de herramientas final: rich:toolBar en el modo
vista:
Nuestro nuevo panel está listo, vamos a ver cómo se ve en el modo vista:
Resumen
En este capítulo, hemos terminado nuestra aplicación.