PrettyFaces és una llibreria open-source per re-escriure les URL (URL-Rewriting) i fer-les més amigables amb suport millorat per JSF 1.1, 1.2 i 2.0. PrettyFaces resol el problema "URL REST" de forma elegant, i a més incorpora característiques com: accions de càrrega de pàgina, integració perfecte amb la navegació JSF i compatibilitat sense configuració amb d'altres frameworks web.
El primer pas és afegir les dependències al projecte Maven. L'arquetipo del FAM ja les incorpora al pom.xml
<!-- Pretty URL --> <dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-servlet</artifactId> <version>2.0.12.Final</version> </dependency> <dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-config-prettyfaces</artifactId> <version>${prettyfaces.version}</version> </dependency> <!-- End PrettyFaces -->
A continuació, a l'arxiu web.xml
, cal indicar els fitxers que defineixen les rutes de la nostra aplicació sota el paràmetre com.ocpsoft.pretty.CONFIG_FILES
. Aquí tenim l'exemple del FAM:
<!-- Configuració PretyFaces --> <context-param> <param-name>com.ocpsoft.pretty.CONFIG_FILES</param-name> <param-value>/WEB-INF/pretty-aplicacio.xml,/WEB-INF/pretty-index.xml,/WEB-INF/pretty-demos.xml</param-value> </context-param> <context-param> <param-name>com.ocpsoft.pretty.DEVELOPMENT</param-name> <param-value>true</param-value> </context-param>
Per mostrar com es configura la navegació farem servir com a exemple els següents valors:
EntitatBase
WebRoot/views
aplicacions.diba.cat
fambo
A continuació la taula que defineix la relació entre una petició HTTP i l'acció que es desencadena en el cas d'operacions amb les entitats i la base de dades, bàsicament lo que s'anomena CRUD: Create (crea), Read (llegeix), Update (actualitza) i Destroy (suprimeix).
Verb HTTP | Ruta | BackingBean::mètode | Usat per |
---|---|---|---|
GET | /entitatsBase | entitatBaseBB::index | Retorna la vista on apareixen totes les entitats base |
GET | /entitatsBase/crea | entitatBaseBB::create | Retorna la vista per crear una nova entitat base |
POST | /entitatsBase/crea | entitatBaseBB::saveOrUpdate | Crea (desa) una nova entitat base |
GET | /entitatsBase/:id/edita | entitatBaseBB::edit | Retorna la vista per editar una entitat base que ja existeix |
POST | /entitatsBase/:id/edita | entitatBaseBB::saveOrUpdate | Actualitza (desa) una entitat base concreta |
POST | /entitatsBase/:id/edita | entitatBaseBB::delete | Suprimeix una entitat base concreta |
En JSF, és el BackingBean i el mètode qui determina l'accio a realitzar. Per aquest motiu, una mateixa ruta i verb poden tenir diferents resultats segons el mètode a executar.
Una de les avantages de PrettyFaces
és el fet de poder definir una URL que executa un mètode d'un Backing Bean. Per exemple, en determinades etiquetes JSF no està disponible l'atribut action
o actionListener
. O en d'altres casos, la URL és una etiqueta HTML que no permet afegir cap referència a Java, però amb la definició de la navegació amb PrettyFaces
si s'executarà el mètode desitjat.
La següent taula mostra les accions de navegació definides per FAMEntitatBase, la adreça URL corresponent i la configuració de PrettyFaces
:
Acció de navegació | Ruta (URL) | PrettyFaces |
---|---|---|
Índex de les entitats | /entitatsBase |
<url-mapping id="entitatsBase"> <pattern value="/entitatsBase" /> <view-id value="/views/entitatBase/entitatBaseLlista.xhtml" /> </url-mapping> |
Inicialitza la nova entitat base i retorna la vista HTML | /entitatsBase/crea |
<url-mapping id="newEntitatBase"> <pattern value="/entitatsBase/crea" /> <view-id value="/views/entitatBase/entitatBaseDetall.xhtml" /> <!-- Evita executar l'acció amb submit o Ajax, només amb GET --> <action onPostback="false">#{entitatBaseBB.create}</action> </url-mapping> |
Inicialitza la edició d'una entitat base i retorna la vista HTML | /entitatsBase/{id}/edita |
<url-mapping id="viewEntitatBase"> <pattern value="/entitatsBase/#{id}/edita" /> <view-id value="/views/entitatBase/entitatBaseDetall.xhtml" /> <!-- Evita executar l'acció amb submit o Ajax, només amb GET --> <action onPostback="false">#{entitatBaseBB.edit}</action> </url-mapping> |
Inicialitza la duplicació d'una entitat base i retorna la vista HTML | /entitatsBase/{id}/duplica |
<url-mapping id="duplicaEntitatBase"> <pattern value="/entitatsBase/#{id}/duplica" /> <view-id value="/views/entitatBase/entitatBaseDetall.xhtml" /> <!-- Evita executar l'acció amb submit o Ajax, només amb GET --> <action onPostback="false">#{entitatBaseBB.duplica}</action> </url-mapping> |
Mostra un dialeg. Útil per seleccionar una entitat base des d'una altra pantalla | /entitatsBase/dialog |
<url-mapping id="dialogEntitatBase"> <pattern value="/entitatsBase/dialog"></pattern> <view-id value="/views/entitatBase/entitatBaseDialog.xhtml" /> </url-mapping> |
Quan arriba una petició, el servlet de PrettyFaces
verifica si la URL coincideix amb algun dels patrons configurats. Si és així, llavors primer executa l'acció, si en té d'alguna definida, i a continuació retorna la vista corresponent.
Per generar la URL correcta a les vistes (arxius xhtml
) i els Backing Bean, només cal escriure "pretty:<view_id>"
Exemples:
<h:link outcome="pretty:entitatsBase" value="Índex" />
Enllaç a la pàgina índex des d'un bean:
public String index() { return "pretty:entitatsBase"; }
id
) coincideix amb el nom que s'ha definit a la navegació <pattern value="/entitatsBase/#{id}/edita" /><p:column headerText="Assumpte" sortBy="#{element.famEntAssumpte}"> <h:link outcome="pretty:viewEntitatBase" value="#{element.famEntAssumpte}"> <f:param name="id" value="#{element.id}"></f:param> </h:link> </p:column>
Cal tenir en compte que si no troba cap regla de navegació que coincideixi amb la ruta, ja sigui definida amb PrettyFaces o amb JSF, llavors torna a mostrar la mateixa vista.
El flux normal de navegació és el que mostra la següent imatge:
El menú principal està definit a l'arxiu menuAplicacio.xhtml
i caldria afegir un enllaç com aquest:
<h:link outcome="pretty:entitatsBase">#{literalsAplicacio['Menu.EntitatBase']}</h:link>
<p:column headerText="Assumpte"> <h:link outcome="pretty:viewEntitatBase" value="#{element.famEntAssumpte}"> <f:param name="id" value="#{element.id}"></f:param> </h:link> </p:column>
<p:commandLink action="#{entitatBaseBB.saveOrUpdate}" id="save" ajax="true" styleClass="btn btn-default" type="submit" update="@form messages"> <i class="fa fa-save" aria-hidden="true" /> #{literalsCore['Helper.Save']} </p:commandLink>
<h:link styleClass="btn btn-default" outcome="pretty:entitatsBase"> <i class="fa fa-undo" aria-hidden="true" /> #{literalsCore['Helper.Back']} </h:link>
<p:commandLink action="#{entitatBaseBB.delete}" styleClass="btn btn-danger" immediate="true"> <i class="fa fa-trash" aria-hidden="true" /> #{literalsCore['Helper.Destroy']} <p:confirm header="Confirmació" message="#{literalsCore['Helper.Destroy.Confirm']}" /> </p:commandLink>
El diàleg de confirmació està definit com
<p:confirmDialog global="true" showEffect="fade" hideEffect="fade"> <p:commandButton value="#{literalsCore['Helper.Yes']}" type="button" styleClass="ui-confirmdialog-yes btn btn-default" icon="fa fa-check" /> <p:commandButton value="#{literalsCore['Helper.No']}" type="button" styleClass="ui-confirmdialog-no btn btn-default" icon="fa fa-close" /> </p:confirmDialog>
Enllaços:
[1] http://www.ocpsoft.org/prettyfaces/