FAM 4.0 - Navegacio

1. Què és PrettyFaces?

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.

2. Configuració

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:

  • El model/entitat és EntitatBase
  • Les pàgines JSF es troben al directori WebRoot/views
  • El domini és aplicacions.diba.cat
  • El context és fambo

3.1. Rutes, verbs HTTP i accions

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 HTTPRutaBackingBean::mètodeUsat per
GET/entitatsBaseentitatBaseBB::indexRetorna la vista on apareixen totes les entitats base
GET/entitatsBase/creaentitatBaseBB::createRetorna la vista per crear una nova entitat base
POST/entitatsBase/creaentitatBaseBB::saveOrUpdateCrea (desa) una nova entitat base
GET/entitatsBase/:id/editaentitatBaseBB::editRetorna la vista per editar una entitat base que ja existeix
POST/entitatsBase/:id/editaentitatBaseBB::saveOrUpdateActualitza (desa) una entitat base concreta
POST/entitatsBase/:id/editaentitatBaseBB::deleteSuprimeix 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.

3.2. Definició de les rutes

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>

 

3.3. Com funciona?

3.3.1. Entrada

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.

3.3.2. Sortida

Per generar la URL correcta a les vistes (arxius xhtml) i els Backing Bean, només cal escriure "pretty:&lt;view_id&gt;"

Exemples:

  • Enllaç a la pàgina índex en HTML
 <h:link outcome="pretty:entitatsBase" value="Índex" />
  • Enllaç a la pàgina índex des d'un bean:

public String index() {
  return "pretty:entitatsBase";
}
  • Enllaç al detall des de l'index (fila). El nom del paràmetre (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.

4. Flux de navegació

El flux normal de navegació és el que mostra la següent imatge:

4.1. Exemples d'enllaços

4.1.1. Anar a l'Índex de les entitats des del menú principal

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>

4.1.2. Vista de llista o índex: edició de l'entitat

<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>        

4.1.3. Vista de detall

4.1.3.1. Actualitza o crea l'entitat
<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>
4.1.3.2. Torna a l'índex
<h:link styleClass="btn btn-default" outcome="pretty:entitatsBase">
  <i class="fa fa-undo" aria-hidden="true" />
  #{literalsCore['Helper.Back']}
</h:link>        
4.1.3.3. Suprimeix l'entitat amb confirmació
<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>
1
Grups de treball:
Plataforma JEE