Skip to content

12. Versão 7 - Aplicação Web PAM com múltiplas vistas/múltiplas páginas

Aqui voltamos à arquitetura inicial, na qual a camada [de negócios] foi simulada. Sabemos agora que esta pode ser facilmente substituída pela camada [de negócios] real. A camada [de negócios] simulada facilita os testes.

Uma aplicação JSF é do tipo MVC (Model-View-Controller):

  • o [Faces Servlet] é o controlador genérico fornecido pelo JSF. Este controlador é estendido por manipuladores de eventos específicos da aplicação. Os manipuladores de eventos encontrados até agora eram métodos de classes que serviam como modelos para páginas JSF
  • As páginas JSF enviam respostas para o navegador do cliente. Estas são as vistas da aplicação.
  • As páginas JSF contêm elementos dinâmicos conhecidos como o modelo da página. Note-se que, para alguns autores, o modelo abrange as entidades manipuladas pela aplicação, tais como as classes PayrollSheet ou Employee. Para distinguir entre estes dois modelos, podemos referir-nos ao modelo da aplicação e ao modelo da página JSF.

Na arquitetura JSF, a transição de uma página JSFi para uma página JSFj pode ser problemática.

  • A página JSFi foi apresentada. A partir desta página, o utilizador aciona um POST através de algum evento [1]
  • No JSF, este pedido POST é geralmente tratado [2a,2b] por um método C do modelo Mi da página JSFi. Podemos dizer que o método C é um controlador secundário.
  • Se, no final deste método, a página JSFj precisar de ser apresentada, o controlador C deve:
  1. atualizar [2c] o modelo Mj da página JSFj
  2. devolver [2a] ao controlador principal a chave de navegação que permitirá que a página JSFj seja exibida

O Passo 1 requer que o modelo Mi da página JSFi tenha uma referência ao modelo Mj da página JSFj. Isto complica um pouco as coisas, tornando os modelos Mi dependentes uns dos outros. De facto, o gestor C do modelo Mi que atualiza o modelo Mj deve estar ciente deste último. Se o modelo Mj precisar de ser alterado, o gestor C do modelo Mi também precisará de ser alterado.

Existe um cenário em que as dependências de modelos podem ser evitadas: quando há um único modelo M utilizado por todas as páginas JSF. Esta arquitetura é adequada apenas para aplicações com poucas vistas, mas é muito fácil de utilizar. É esta que estamos a utilizar atualmente.

Neste contexto, a arquitetura da aplicação é a seguinte:

12.1. As vistas da aplicação

As diferentes vistas apresentadas ao utilizador serão as seguintes:

  • a vista [VueSaisies], que exibe o formulário de simulação

  • a vista [VueSimulation], utilizada para apresentar os resultados detalhados da simulação:

Image

Image

  • a vista [SimulationsView], que lista as simulações realizadas pelo cliente

Image

  • a [EmptySimulationsView], que indica que o cliente não tem simulações ou já não tem mais simulações:

Image

  • a vista [ErrorView], que indica um ou mais erros:

Image

12.2. O projeto NetBeans para a camada [web]

O projeto NetBeans para esta versão será o seguinte projeto Maven:

  • em [1], os ficheiros de configuração,
  • em [2], as páginas JSF,
  • em [3], a folha de estilo e a imagem de fundo para as visualizações,
  • em [4], as classes da camada [web],
  • em [5], as camadas inferiores da aplicação,
  • em [6], o ficheiro de mensagens para a internacionalização da aplicação,
  • em [7], as dependências do projeto.

Vamos rever alguns destes elementos.

12.2.1. Ficheiros de configuração

Os ficheiros [web.xml] e [faces-config.xml] são idênticos aos do projeto anterior, com exceção de um detalhe no [web.xml]:


<dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
            <version>${project.version}</version>
            <type>ejb</type>
        </dependency>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>mv-pam-jsf2-ejb</artifactId>
            <version>${project.version}</version>
            <type>war</type>
        </dependency>
    </dependencies>
  • linhas 4-6: a página inicial é a página [saisie.xhtml]

12.2.2. A folha de estilo

O ficheiro [styles.css] é o seguinte:


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  ...
  <welcome-file-list>
    <welcome-file>faces/saisie.xhtml</welcome-file>
  </welcome-file-list>
  ...
</web-app>

Aqui estão alguns exemplos de código JSF que utilizam estes estilos:

Visualização de Simulações


.simulationsHeader {
   text-align: center;
   font-style: italic;
   color: Snow;
   background: Teal;
}
 
.simuNum {
   height: 25px;
   text-align: center;
   background: MediumTurquoise;
}
.simuNom {
   text-align: left;
   background: PowderBlue;
}
.simuPrenom {
   width: 6em;
   text-align: left;
   color: Black;
   background: MediumTurquoise;
}
.simuHT {
   width: 3em;
   text-align: center;
   color: Black;
   background: PowderBlue;
}
.simuJT {
   width: 3em;
   text-align: center;
   color: Black;
   background: MediumTurquoise;
}
.simuSalaireBase {
   width: 9em;
   text-align: center;
   color: Black;
   background: PowderBlue;
}
.simuIndemnites {
   width: 3em;
   text-align: center;
   color: Black;
   background: MediumTurquoise;
}
.simuCotisationsSociales {
   width: 6em;
   text-align: center;
   background: PowderBlue;
}
 
.simuSalaireNet {
   width: 10em;
   text-align: center;
   background: MediumTurquoise;
}
 
.erreursHeaders {
   background: Teal;
   background-color: #ff6633;
   color: Snow;
   font-style: italic;
   text-align: center
 
}
 
.erreurClasse {
   background: MediumTurquoise;
   background-color: #ffcc66;
   height: 25px;
   text-align: center
}
 
.erreurMessage {
   background: PowderBlue;
   background-color: #ffcc99;
   text-align: left
}
          <h:dataTable value="#{form.simulations}" var="simulation"

O resultado é o seguinte:

Image

Visualização de erros


                       headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
          <h:dataTable value="#{form.erreurs}" var="erreur"

Image

12.2.3. O ficheiro de mensagens

O ficheiro de mensagens [messages_fr.properties] é o seguinte:


                                 headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">

12.2.4. A camada [de negócios]

A camada [de negócios] simulada é modificada da seguinte forma:


form.titre=Simulateur de calcul de paie
form.comboEmployes.libell\u00e9=Employ\u00e9
form.heuresTravaill\u00e9es.libell\u00e9=Heures travaill\u00e9es
form.joursTravaill\u00e9s.libell\u00e9=Jours travaill\u00e9s
form.heuresTravaill\u00e9es.required=Indiquez le nombre d'heures travaill\u00e9es
form.heuresTravaill\u00e9es.validation=Donn\u00e9e incorrecte
form.joursTravaill\u00e9s.required=Indiquez le nombre de jours travaill\u00e9s
form.joursTravaill\u00e9s.validation=Donn\u00e9e incorrecte
form.btnSalaire.libell\u00e9=Salaire
form.btnRaz.libell\u00e9=Raz
exception.header=L'exception suivante s'est produite
exception.httpCode=Code HTTP de l'erreur
exception.message=Message de l'exception
exception.requestUri=Url demand\u00e9e lors de l'erreur
exception.servletName=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
form.infos.employ\u00e9=Informations Employ\u00e9
form.employe.nom=Nom
form.employe.pr\u00e9nom=Pr\u00e9nom
form.employe.adresse=Adresse
form.employe.ville=Ville
form.employe.codePostal=Code postal
form.employe.indice=Indice
form.infos.cotisations=Informations Cotisations sociales
form.cotisations.csgrds=CSGRDS
form.cotisations.csgd=CSGD
form.cotisations.retraite=Retraite
form.cotisations.secu=S\u00e9curit\u00e9 sociale
form.infos.indemnites=Informations Indemnit\u00e9s
form.indemnites.salaireHoraire=Salaire horaire
form.indemnites.entretienJour=Entretien / Jour
form.indemnites.repasJour=Repas / Jour
form.indemnites.cong\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
form.infos.salaire=Informations Salaire
form.salaire.base=Salaire de base
form.salaire.cotisationsSociales=Cotisations sociales
form.salaire.entretien=Indemnit\u00e9s d'entretien
form.salaire.repas=Indemnit\u00e9s de repas
form.salaire.net=Salaire net
form.menu.faireSimulation=| Faire la simulation
form.menu.effacerSimulation=| Effacer la simulation
form.menu.enregistrerSimulation=| Enregistrer la simulation
form.menu.retourSimulateur=| Retour au simulateur
form.menu.voirSimulations=| Voir les simulations
form.menu.terminerSession=| Terminer la session
simulations.headers.nom=Nom
simulations.headers.nom=Nom
simulations.headers.prenom=Pr\u00e9nom
simulations.headers.heuresTravaillees=Heures travaill\u00e9es
simulations.headers.joursTravailles=Jours Travaill\u00e9s
simulations.headers.salaireBase=Salaire de base
simulations.headers.indemnites=Indemnit\u00e9s
simulations.headers.cotisationsSociales=Cotisations sociales
simulations.headers.salaireNet=SalaireNet
simulations.headers.numero=N\u00b0
erreur.titre=Une erreur s'est produite.
erreur.exceptions=Cha\u00eene des exceptions
exception.type=Type de l'exception
exception.message=Message associ\u00e9
  • linha 8: a lista de funcionários
  • linha 7: a mesma lista como um dicionário indexado pelos números de segurança social dos funcionários
  • linhas 24–39: o método findAllEmployees, que devolve a lista de funcionários. Este método cria uma lista codificada e faz referência a ela através do campo employees da linha 8.
  • linhas 27–33: são criadas uma lista e um dicionário com dois funcionários
  • linha 35: um funcionário é adicionado à lista de funcionários (linha 8), mas não ao dicionário hashEmployees (linha 7). Isto faz com que o funcionário apareça no menu suspenso de funcionários, mas não seja reconhecido pelo método calculatePayroll (linha 14), fazendo com que este lance uma exceção (linha 17).
  • Linhas 11–21: o método calculatePayroll
  • Linha 14: O funcionário é procurado no dicionário `hashEmployees` utilizando o seu SSN. Se não for encontrado, é lançada uma exceção (linhas 16–18). Assim, obteremos uma exceção para o funcionário com o SSN X adicionado na linha 35 à lista `employees`, mas não ao dicionário `hashEmployees`.
  • Linha 20: É criado e devolvido um recibo de vencimento fictício.

12.3. Os beans da aplicação

Haverá três beans com três âmbitos diferentes:

  

12.3.1. O bean ApplicationData

O bean ApplicationData terá âmbito de aplicação:


package metier;
 
...
public class Metier implements ImetierLocal, Serializable {
 
  // list of employees
  private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
  private List<Employe> listEmployes;
 
  // get your payslip
  public FeuilleSalaire calculerFeuilleSalaire(String SS,
    double nbHeuresTravaillées, int nbJoursTravaillés) {
    // we get the employee
    Employe e=hashEmployes.get(SS);
    // an exception is thrown if the employee does not exist
    if(e==null){
      throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
    }
    // a fictitious payslip is returned
    return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),e.getIndemnite(),new ElementsSalaire(100,100,100,100,100));
  }
 
  // list of employees
  public List<Employe> findAllEmployes() {
    if(listEmployes==null){
      // create a list of three employees
      listEmployes=new ArrayList<Employe>();
      listEmployes.add(new Employe("254104940426058","Jouveinal","Marie","5 rue des oiseaux","St Corentin","49203",new Indemnite(2,2.1,2.1,3.1,15)));
      listEmployes.add(new Employe("260124402111742","Laverti","Justine","La brûlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12)));
      // employee dictionary
      for(Employe e:listEmployes){
        hashEmployes.put(e.getSS(),e);
      }
      // we add an employee who doesn't exist in the dictionary
      listEmployes.add(new Employe("X","Y","Z","La brûlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12)));
    }
    // we return the list of employees
    return listEmployes;
  }
}
  • linha 11: a anotação @Named torna a classe um bean gerido. Note que, ao contrário do projeto anterior, não utilizámos a anotação @ManagedBean. A razão é que uma referência a esta classe deve ser injetada noutra classe utilizando a anotação @Inject, e @Inject apenas injeta classes com a anotação @Named.
  • Linha 12: A anotação @ApplicationScoped torna a classe um objeto com âmbito de aplicação. Note-se que a classe da anotação é [javax.enterprise.context.ApplicationScoped] (linha 6) e não [javax.faces.bean.ApplicationScoped], como nos beans do projeto anterior.

O bean ApplicationData tem duas finalidades:

  • linha 16: manter uma referência à camada [business],
  • linhas 18–19: definir um logger que pode ser utilizado por outros beans para registar informações na consola do GlassFish.

12.3.2. O bean SessionData

O bean SessionData terá âmbito de sessão:


package web.beans.application;
 
import java.io.Serializable;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import metier.IMetierLocal;
import metier.Metier;
 
@Named
@ApplicationScoped
public class ApplicationData implements Serializable {
 
  // business layer
  private IMetierLocal metier = new Metier();
  // logger
  private boolean logsEnabled = true;
  private final Logger logger = Logger.getLogger("pam");
 
  public ApplicationData() {
  }
 
  @PostConstruct
  public void init() {
    // log
    if (isLogsEnabled()) {
      logger.info("ApplicationData");
    }
  }
 
  // getters and setters
...
}
  • Linha 13: A classe SessionData é um bean gerenciado (@Named) que pode ser injetado em outros beans gerenciados,
  • linha 14: tem âmbito de sessão (@SessionScoped),
  • linhas 18–19: uma referência ao bean ApplicationData é injetada nela (@Inject),
  • linhas 21–32: os dados da aplicação que devem ser mantidos entre sessões.
  • linha 21: a lista de simulações realizadas pelo utilizador,
  • linha 22: o número da última simulação guardada,
  • linha 23: a última simulação que foi realizada,
  • linhas 25–30: as opções do menu,
  • linha 32: a localização da aplicação.

Linhas 39–44: o método init é executado após a instância da classe (@PostConstruct). Aqui, ele é usado apenas para registar a sua execução. Temos de poder verificar que ele é executado apenas uma vez por utilizador, uma vez que a classe tem âmbito de sessão. Linha 42: o método utiliza o logger definido na classe ApplicationData. É por isso que precisámos de injetar uma referência a este bean (linhas 18–19).

12.3.3. O Bean de Formulário

O bean de formulário tem âmbito de solicitação:


package web.beans.session;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import web.beans.application.ApplicationData;
import web.entities.Simulation;
 
@Named
@SessionScoped
public class SessionData implements Serializable {
 
  // application
  @Inject
  private ApplicationData applicationData;
  // simulations
  private List<Simulation> simulations = new ArrayList<Simulation>();
  private int numDerniereSimulation = 0;
  private Simulation simulation;
  // menus
  private boolean menuFaireSimulationIsRendered = true;
  private boolean menuEffacerSimulationIsRendered = true;
  private boolean menuEnregistrerSimulationIsRendered;
  private boolean menuVoirSimulationsIsRendered;
  private boolean menuRetourSimulateurIsRendered;
  private boolean menuTerminerSessionIsRendered = true;
  // local
  private String locale="fr_FR";
 
  // manufacturer
  public SessionData() {
  }
 
  @PostConstruct
  public void init() {
    // log
    if (applicationData.isLogsEnabled()) {
      applicationData.getLogger().info("SessionData");
    }
  }
 
  // menu management
  public void setMenu(boolean menuFaireSimulationIsRendered, boolean menuEnregistrerSimulationIsRendered, boolean menuEffacerSimulationIsRendered, boolean menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean menuTerminerSessionIsRendered) {
    this.setMenuFaireSimulationIsRendered(menuFaireSimulationIsRendered);
    this.setMenuEnregistrerSimulationIsRendered(menuEnregistrerSimulationIsRendered);
    this.setMenuVoirSimulationsIsRendered(menuVoirSimulationsIsRendered);
    this.setMenuEffacerSimulationIsRendered(menuEffacerSimulationIsRendered);
    this.setMenuRetourSimulateurIsRendered(menuRetourSimulateurIsRendered);
    this.setMenuTerminerSessionIsRendered(menuTerminerSessionIsRendered);
  }
 
  // getters and setters
  ...
}
  • linha 17, a classe é um bean gerido (@Named),
  • linha 18, com âmbito de solicitação (@RequestScoped),
  • linhas 24–25: injeção de uma referência ao bean com escopo de aplicação ApplicationData,
  • linhas 26–27: injeção de uma referência ao bean SessionData com escopo de sessão.

12.4. As páginas da aplicação

  

12.4.1. [layout.xhtml]

A página [layout.xhtml] controla o layout de todas as visualizações:


package web.beans.request;
 
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import jpa.Employe;
import metier.FeuilleSalaire;
import web.beans.application.ApplicationData;
import web.beans.session.*;
import web.entities.Erreur;
import web.entities.Simulation;
 
@Named
@RequestScoped
public class Form {
 
  public Form() {
  }
  // other beans
  @Inject
  private ApplicationData applicationData;
  @Inject
  private SessionData sessionData;
  // the view model
  private String comboEmployesValue = "";
  private String heuresTravaillées = "";
  private String joursTravaillés = "";
  private Integer numSimulationToDelete;
  private List<Erreur> erreurs = new ArrayList<Erreur>();
  private FeuilleSalaire feuilleSalaire;
 
  
  // list of employees
  public List<Employe> getEmployes(){
    return applicationData.getMetier().findAllEmployes();
  }
 
  // menu action
  public String faireSimulation() {
    ...
  }
 
  public String enregistrerSimulation() {
  ...
  }
 
  public String effacerSimulation() {
  ...
  }
 
  public String voirSimulations() {
  ...
  }
 
  public String retourSimulateur() {
   ...
  }
 
  public String terminerSession() {
  ...
  }
 
  public String retirerSimulation() {
 ...
  }
 
  private void razFormulaire() {
 ...
  }
 
// getters and setters
...
}

Cada vista é composta pelos seguintes elementos:

  • um cabeçalho exibido pelo fragmento [entete.xhtml] (linha 24),
  • um corpo composto por dois fragmentos denominados part1 (linhas 26–28) e part2 (linha 29),
  • linha 8: o atributo locale garante a internacionalização das páginas. Aqui, haverá apenas um: fr_FR,
  • linhas 14–19: código JavaScript.

A apresentação da página [layout.xhtml] é a seguinte:

  • em [1], o cabeçalho [entete.xhtml],
  • em [2], o fragmento part1.

12.4.2. O cabeçalho [entete.xhtml]

O código da página [entete.xhtml] é o seguinte:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <f:view locale="#{sessionData.locale}">
    <h:head>
      <title><h:outputText value="#{msg['form.titre']}"/></title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <script type="text/javascript">
      function raz(){
        // on change les valeurs postées
        document.forms['formulaire'].elements['formulaire:comboEmployes'].value="0";
        document.forms['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
        document.forms['formulaire'].elements['formulaire:joursTravailles'].value="0";
      }
    </script>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
      <h:form id="formulaire">
        <!-- entete -->
        <ui:include src="entete.xhtml" />
        <!-- content -->
        <ui:insert name="part1" >
          Gestion des assistantes maternelles
        </ui:insert>
        <ui:insert name="part2"/>
      </h:form>
    </h:body>
  </f:view>
</html>
  • linha 12: o título da aplicação,
  • linhas 16–21: os seis links correspondentes às seis ações que o utilizador pode realizar. Estes links são controlados (atributo rendered) por valores booleanos no bean SessionData,
  • linha 17: clicar no link [Limpar Simulação] aciona a execução da função JavaScript raz. Esta função foi definida no modelo [layout.xhtml],
  • linha 18: o atributo immediate=true garante que a validade dos dados não seja verificada antes da execução do método [Form].enregistrerSimulation. Isto é intencional. Pode querer guardar a última simulação realizada há um minuto, mesmo que os dados introduzidos desde então no formulário (mas ainda não validados) sejam inválidos. O mesmo se aplica às ações [View Simulations], [Clear Simulation], [Return to Simulator] e [End Session].

12.5. Casos de utilização da aplicação

12.5.1. Exibição da página inicial

A página inicial é a seguinte página [saisie.xhtml]:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <ui:composition>
    <!-- entete -->
    <h:panelGrid columns="2">
      <h:panelGroup>
        <h2><h:outputText value="#{msg['form.titre']}"/></h2>
      </h:panelGroup>
      <h:panelGroup>
        <h:panelGrid columns="1">
          <h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>
          <h:commandLink id="cmdEffacerSimulation"  onclick="raz()" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>
          <h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}" rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>
          <h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>
          <h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>
          <h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['form.menu.terminerSession']}" action="#{form.terminerSession}" rendered="#{sessionData.menuTerminerSessionIsRendered}"/>
        </h:panelGrid>
      </h:panelGroup>
    </h:panelGrid>
    <hr/>
  </ui:composition>
</html>
  • linha 8: é exibido na página [layout.xhtml],
  • linha 9: no lugar do fragmento denominado part1. Neste fragmento, a página [saisie2.xhtml] é exibida:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <ui:composition template="layout.xhtml">
    <ui:define name="part1">
      <ui:include src="saisie2.xhtml"/>
    </ui:define>
  </ui:composition>
</html>

Este código apresenta a seguinte visualização:

 

Pergunta: Complete a linha 13 do código XHTML. A lista de itens na caixa de seleção de funcionários é fornecida por um método do bean [Form]. Escreva este método. Os itens exibidos na caixa de seleção terão a propriedade itemValue definida como o número de segurança social do funcionário, e a propriedade itemLabel será uma cadeia de caracteres composta pelo nome e apelido do funcionário.



Pergunta: Como deve o bean [SessionData] ser inicializado para que, durante o pedido GET inicial feito ao formulário, o menu de cabeçalho seja o apresentado acima?


12.5.2. A ação [ faireSimulation]

O código JSF para a ação [runSimulation] é o seguinte:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <h:panelGrid columns="3">
    <h:outputText value="#{msg['form.comboEmployes.libellé']}"/>
    <h:outputText value="#{msg['form.heuresTravaillées.libellé']}"/>
    <h:outputText value="#{msg['form.joursTravaillés.libellé']}"/>
    <h:selectOneMenu id="comboEmployes" value="#{form.comboEmployesValue}">
      <f:selectItems .../>
    </h:selectOneMenu>
    <h:inputText id="heuresTravaillees" value="#{form.heuresTravaillées}" required="true" requiredMessage="#{msg['form.heuresTravaillées.required']}" validatorMessage="#{msg['form.heuresTravaillées.validation']}">
      <f:validateDoubleRange minimum="0" maximum="300"/>
    </h:inputText>
    <h:inputText id="joursTravailles" value="#{form.joursTravaillés}" required="true" requiredMessage="#{msg['form.joursTravaillés.required']}" validatorMessage="#{msg['form.joursTravaillés.validation']}">
      <f:validateLongRange minimum="0" maximum="31"/>
    </h:inputText>
    <h:panelGroup></h:panelGroup>
    <h:message for="heuresTravaillees" styleClass="error"/>
    <h:message for="joursTravailles" styleClass="error"/>
  </h:panelGrid>
  <hr/>
</html>

A ação [faireSimulation] calcula um recibo de vencimento:

Image

A partir da página anterior, obtemos o seguinte resultado:

A simulação é apresentada na seguinte página [simulation.xhtml]:


<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>
  • Linha 8: a página [simulation.xhtml] é inserida na página [layout.xhtml],
  • linhas 9–11: a área de entrada é apresentada no fragmento part1 da página de layout,
  • linhas 12–91: a simulação é apresentada no fragmento part2 da página de layout.

Pergunta: Escreva o método [doSimulation] para a classe [Form]. A simulação será guardada no bean SessionData.


12.5.3. Tratamento de erros

Queremos ser capazes de tratar adequadamente as exceções que possam ocorrer durante o cálculo de uma simulação. Para tal, o código do método [runSimulation] utilizará um bloco try/catch:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <ui:composition template="layout.xhtml">
    <ui:define name="part1">
      <ui:include src="saisie2.xhtml"/>
    </ui:define>
    <ui:define name="part2">
      <h:outputText value="#{msg['form.infos.employé']}" styleClass="titreInfos"/>
      <br/><br/>
      <h:panelGrid columns="3" rowClasses="libelle,info">
        <h:outputText value="#{msg['form.employe.nom']}"/>
        <h:outputText value="#{msg['form.employe.prénom']}"/>
        <h:outputText value="#{msg['form.employe.adresse']}"/>
        <h:outputText value="#{form.feuilleSalaire.employe.nom}"/>
        <h:outputText value="#{form.feuilleSalaire.employe.prenom}"/>
        <h:outputText value="#{form.feuilleSalaire.employe.adresse}"/>
      </h:panelGrid>
      <h:panelGrid columns="3" rowClasses="libelle,info">
        <h:outputText value="#{msg['form.employe.ville']}"/>
        <h:outputText value="#{msg['form.employe.codePostal']}"/>
        <h:outputText value="#{msg['form.employe.indice']}"/>
        <h:outputText value="#{form.feuilleSalaire.employe.ville}"/>
        <h:outputText value="#{form.feuilleSalaire.employe.codePostal}"/>
        <h:outputText value="#{form.feuilleSalaire.employe.indemnite.indice}"/>
      </h:panelGrid>
      <br/>
      <h:outputText value="#{msg['form.infos.cotisations']}" styleClass="titreInfos"/>
      <br/><br/>
      <h:panelGrid columns="4" rowClasses="libelle,info">
        <h:outputText value="#{msg['form.cotisations.csgrds']}"/>
        <h:outputText value="#{msg['form.cotisations.csgd']}"/>
        <h:outputText value="#{msg['form.cotisations.retraite']}"/>
        <h:outputText value="#{msg['form.cotisations.secu']}"/>
        <h:outputText value="#{form.feuilleSalaire.cotisation.csgrds} %"/>
        <h:outputText value="#{form.feuilleSalaire.cotisation.csgd} %"/>
        <h:outputText value="#{form.feuilleSalaire.cotisation.retraite} %"/>
        <h:outputText value="#{form.feuilleSalaire.cotisation.secu} %"/>
      </h:panelGrid>
      <br/>
      <h:outputText value="#{msg['form.infos.indemnites']}" styleClass="titreInfos"/>
      <br/><br/>
      <h:panelGrid columns="4" rowClasses="libelle,info">
        <h:outputText value="#{msg['form.indemnites.salaireHoraire']}"/>
        <h:outputText value="#{msg['form.indemnites.entretienJour']}"/>
        <h:outputText value="#{msg['form.indemnites.repasJour']}"/>
        <h:outputText value="#{msg['form.indemnites.congésPayés']}"/>
        <h:outputFormat value="{0,number,currency}">
          <f:param value="#{form.feuilleSalaire.employe.indemnite.baseHeure}"/>
        </h:outputFormat>
        <h:outputFormat value="{0,number,currency}">
          <f:param value="#{form.feuilleSalaire.employe.indemnite.entretienJour}"/>
        </h:outputFormat>
        <h:outputFormat value="{0,number,currency}">
          <f:param value="#{form.feuilleSalaire.employe.indemnite.repasJour}"/>
        </h:outputFormat>
        <h:outputText value="#{form.feuilleSalaire.employe.indemnite.indemnitesCP} %"/>
      </h:panelGrid>
      <br/>
      <h:outputText value="#{msg['form.infos.salaire']}" styleClass="titreInfos"/>
      <br/><br/>
      <h:panelGrid columns="4" rowClasses="libelle,info">
        <h:outputText value="#{msg['form.salaire.base']}"/>
        <h:outputText value="#{msg['form.salaire.cotisationsSociales']}"/>
        <h:outputText value="#{msg['form.salaire.entretien']}"/>
        <h:outputText value="#{msg['form.salaire.repas']}"/>
        <h:outputFormat value="{0,number,currency}">
          <f:param value="#{form.feuilleSalaire.elementsSalaire.salaireBase}"/>
        </h:outputFormat>
        <h:outputFormat value="{0,number,currency}">
          <f:param value="#{form.feuilleSalaire.elementsSalaire.cotisationsSociales}"/>
        </h:outputFormat>
        <h:outputFormat value="{0,number,currency}">
          <f:param value="#{form.feuilleSalaire.elementsSalaire.indemnitesEntretien}"/>
        </h:outputFormat>
        <h:outputFormat value="{0,number,currency}">
          <f:param value="#{form.feuilleSalaire.elementsSalaire.indemnitesRepas}"/>
        </h:outputFormat>
      </h:panelGrid>
      <br/>
      <h:panelGrid columns="3" columnClasses="libelle,col2,info">
        <h:outputText value="#{msg['form.salaire.net']}"/>
        <h:panelGroup></h:panelGroup>
        <h:outputFormat value="{0,number,currency}">
          <f:param value="#{form.feuilleSalaire.elementsSalaire.salaireNet}"/>
        </h:outputFormat>
      </h:panelGrid>
    </ui:define>
  </ui:composition>
</html>

A lista de erros criados na linha 15 é a seguinte:


  // action du menu
  public String faireSimulation(){
    try{
      // on calcule la feuille de salaire
      feuilleSalaire= ...
      // on affiche la simulation
      ...
      // on met à jour le menu
      ...
      // on rend la vue simulation
      return "simulation";
    }catch(Throwable th){
      // on vide la liste des erreurs précédentes
      ...
      // on crée la nouvelle liste des erreurs
      ...
      // on affiche la vue vueErreur
      ...
      // on met à jour le menu
      ...
      // on affiche la vue erreur
      return "erreurs";
    }
}

A classe Error é definida da seguinte forma:


  // le modèle des vues
  ...
  private List<Erreur> erreurs=new ArrayList<Erreur>();
...

Os erros serão exceções cujo nome de classe é armazenado no campo class e cuja mensagem é armazenada no campo message.

A lista de erros construída no método [faireSimulation] consiste em:

  • a exceção inicial th do tipo Throwable que ocorreu,
  • a sua causa th.getCause(), se tiver uma,
  • a causa da causa h.getCause().getCause(), se existir.
  • ...

Aqui está um exemplo de uma lista de erros:

Image

Acima, o funcionário [Z Y] não existe no dicionário de funcionários utilizado pela camada [de negócios] simulada. Neste caso, a camada [de negócios] simulada lança uma exceção (linha 6 abaixo):


package web.entities;
 
public class Erreur {
 
  public Erreur() {
  }
 
  // field
  private String classe;
  private String message;

  // manufacturer
  public Erreur(String classe, String message){
    this.setClasse(classe);
    this.message=message;
  }
 
  // getters and setters
...  
}

O resultado é o seguinte:

Image

Esta vista é apresentada pela seguinte página [errors.xhtml]:


  public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
    // we get the employee
    Employe e=hashEmployes.get(SS);
    // an exception is thrown if the employee does not exist
    if(e==null){
      throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
    }
...
}

Pergunta: Complete o método [faireSimulation] de forma a que, em caso de exceção, ele exiba a vista [vueErreur].


12.5.4. A ação [ effacerSimulation]

A ação [clearSimulation] permite ao utilizador regressar a um formulário vazio:

O código JSF para o link [clearSimulation] é o seguinte:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <ui:composition template="layout.xhtml">
    <ui:define name="part1">
      <h3><h:outputText value="#{msg['erreur.titre']}"/></h3>
      <h:dataTable value="#{form.erreurs}" var="erreur"
                   headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">
        <f:facet name="header">
          <h:outputText value="#{msg['erreur.exceptions']}"/>
        </f:facet>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['exception.type']}"/>
          </f:facet>
          <h:outputText value="#{erreur.classe}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['exception.message']}"/>
          </f:facet>
          <h:outputText value="#{erreur.message}"/>
        </h:column>
      </h:dataTable>
    </ui:define>
  </ui:composition>
</html>

Clicar primeiro na ligação [ClearSimulation] aciona uma chamada à função JavaScript raz(). Este método está definido na página [layout.xhtml]:


<h:commandLink id="cmdEffacerSimulation"  onclick="raz()" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>

As linhas 4–6 alteram os valores enviados. Note que

  • os valores enviados são válidos, ou seja, passarão nas verificações de validação dos campos de entrada hoursWorked e daysWorked.
  • A função `raz` não envia o formulário. Em vez disso, o formulário será enviado através do link `cmdEffacerSimulation`. Este envio ocorrerá após a execução da função JavaScript `raz`.

Durante o envio, os valores enviados seguirão o processo normal: validação e, em seguida, atribuição aos campos do modelo. Estes são os seguintes na classe [Form]:


    <script type="text/javascript">
      function raz(){
        // change posted values
        document.forms['formulaire'].elements['formulaire:comboEmployes'].value="0";
        document.forms['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
        document.forms['formulaire'].elements['formulaire:joursTravailles'].value="0";
      }
</script>
  // the view model
  private String comboEmployesValue;
  private String heuresTravaillées;
  private String joursTravaillés;

Estes três campos receberão os três valores lançados {"0", "0", "0"}. Assim que esta atribuição estiver concluída, o método clearSimulation será executado.


Pergunta: Escreva o método [effacerSimulation] para a classe [Form]. Certifique-se de que:

  • apenas o campo de entrada seja exibido,

  • a caixa de combinação esteja definida para o seu primeiro item,

  • os campos de entrada hoursWorked e daysWorked apresentem cadeias de caracteres vazias.


12.5.5. A ação [ enregistrerSimulation]

O código JSF para o link [saveSimulation] é o seguinte:


  ...

A ação [saveSimulation] associada ao link guarda a simulação atual numa lista de simulações mantida na classe [SessionData]:


<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}" rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>

A classe Simulation no e o web.entities é a seguinte:


private List<Simulation> simulations=new ArrayList<Simulation>();

Esta classe permite guardar uma simulação criada pelo utilizador:

  • linha 11: o número da simulação,
  • linha 12: o recibo de vencimento que foi calculado,
  • linha 13: o número de horas trabalhadas,
  • linha 14: o número de dias trabalhados.

Aqui está um exemplo de um registo:

Image

A partir da página anterior, obtemos o seguinte resultado:

Image

O número da simulação é incrementado a cada novo registo. Pertence ao bean SessionData:


package web.entities;
 
import metier.FeuilleSalaire;
 
public class Simulation {
 
  public Simulation() {
  }
 
  // simulation fields
  private Integer num;
  private FeuilleSalaire feuilleSalaire;
  private String heuresTravaillées;
  private String joursTravaillés;
 
  // manufacturer
  public Simulation(Integer num,String heuresTravaillées, String joursTravaillés, FeuilleSalaire feuilleSalaire){
    this.setNum(num);
    this.setFeuilleSalaire(feuilleSalaire);
    this.setHeuresTravaillées(heuresTravaillées);
    this.setJoursTravaillés(joursTravaillés);
  }
 
  public double getIndemnites(){
    return feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+ feuilleSalaire.getElementsSalaire().getIndemnitesRepas();
  }
 
  // getters and setters
...
}
  • Linha 2: o número da última simulação realizada.

O método [saveSimulation] pode proceder da seguinte forma:

  • recuperar o número da última simulação do bean [SessionData] e incrementá-lo,
  • adicionar a nova simulação à lista de simulações mantida pela classe [SessionData],
  • exibir a tabela de simulações:

A tabela de simulação é apresentada na página [simulations.xhtml]:


  // simulations
  private List<Simulation> simulations = new ArrayList<Simulation>();
  private int numDerniereSimulation = 0;
private Simulation simulation;
  • na linha 8, a página [simulations.xhtml] é inserida dentro da página [layout.xhtml],
  • na linha 9, no lugar do fragmento denominado part1,
  • linha 11, a tag <h:dataTable> utiliza o campo #{sessionData.simulations} como fonte de dados, ou seja, o seguinte campo:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <ui:composition template="layout.xhtml">
    <ui:define name="part1">
      <!-- simulation table -->
      <h:dataTable value="#{sessionData.simulations}" var="simulation"
                   headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.numero']}"/>
          </f:facet>
          <h:outputText value="#{simulation.num}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.nom']}"/>
          </f:facet>
          <h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.prenom']}"/>
          </f:facet>
          <h:outputText value="#{simulation.feuilleSalaire.employe.prenom}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.heuresTravaillees']}"/>
          </f:facet>
          <h:outputText value="#{simulation.heuresTravaillées}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.joursTravailles']}"/>
          </f:facet>
          <h:outputText value="#{simulation.joursTravaillés}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.salaireBase']}"/>
          </f:facet>
          <h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.salaireBase}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.indemnites']}"/>
          </f:facet>
          <h:outputText value="#{simulation.indemnites}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.cotisationsSociales']}"/>
          </f:facet>
          <h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.cotisationsSociales}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['simulations.headers.salaireNet']}"/>
          </f:facet>
          <h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.salaireNet}"/>
        </h:column>
        <h:column>
          <h:commandLink value="Retirer" action="#{form.retirerSimulation}">
            <f:setPropertyActionListener target="#{form.numSimulationToDelete}" value="#{simulation.num}"/>
          </h:commandLink>
        </h:column>
      </h:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • O atributo var="simulation" define o nome da variável que representa a simulação atual dentro da tag <h:datatable>

  • O atributo headerClass="simulationsHeaders" define o estilo dos cabeçalhos das colunas da tabela.

  • O atributo columnClasses="...." define o estilo de cada coluna da tabela

Vamos examinar uma das colunas da tabela e ver como está construída:

O código JSF para a coluna Nome é o seguinte:


  // simulations
private List<Simulation> simulations;
  • Linhas 2–4: A tag <f:facet name="header"> define o cabeçalho da coluna
  • linha 5: o nome do funcionário é escrito:
    • simulation refere-se ao atributo var da tag <h:dataTable ...>:

            <h:column>
              <f:facet name="header">
                <h:outputText value="#{msg['simulations.headers.nom']}"/>
              </f:facet>
              <h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
</h:column>

simulation refere-se à simulação atual na lista de simulações: primeiro a 1.ª, depois a 2.ª, …

  • (continuação)
    • simulation.payroll refere-se ao campo de folha de pagamento da simulação atual
    • simulation.payroll.employee refere-se ao campo «employee» dentro do campo «payroll»
    • simulation.payroll.employee.name refere-se ao campo nome do campo funcionário

A mesma técnica é repetida para todas as colunas da tabela. Existe um problema com a coluna «Allowances», que é gerada utilizando o seguinte código:


<h:dataTable value="#{sessionData.simulations}" var="simulation" ...>

Na linha 5, é apresentado o valor de simulation.indemnites. No entanto, a classe Simulation não possui um campo indemnites. É importante lembrar aqui que o campo indemnites não é utilizado diretamente, mas sim através do método simulation.getIndemnites(). Portanto, basta que este método exista. O próprio campo indemnites pode não existir. O método getIndemnites deve devolver o total dos subsídios do funcionário. Isto requer um cálculo intermédio, uma vez que este total não está diretamente disponível no recibo de vencimento. O método getIndemnites é apresentado na Secção 12.5.5.


Pergunta: Escreva o método [enregistrerSimulation] para a classe [Form].


12.5.6. A ação [ retourSimulateur]

O código JSF para o link [retourSimulateur] é o seguinte:


            <h:column>
              <f:facet name="header">
                <h:outputText value="#{msg['simulations.headers.indemnites']}"/>
              </f:facet>
              <h:outputText value="#{simulation.indemnites}"/>
</h:column>

A ação [returnSimulator] associada ao link permite ao utilizador regressar da [simulationsView] para a [inputsView]:

Image

O resultado obtido:

Image


Pergunta: Escreva o método [retourSimulateur] para a classe [Form]. O formulário de entrada apresentado deve estar vazio, tal como acima.


12.5.7. A ação [viewSimulations]

O código JSF para o link [viewSimulations] é o seguinte:


<h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>

A ação [viewSimulations] associada ao link permite ao utilizador visualizar a tabela de simulações, independentemente do estado das suas entradas:

Image

O resultado obtido:

Image


Pergunta: Escreva o método [viewSimulations] para a classe [Form].


Vamos garantir que, se a lista de simulações estiver vazia, a vista apresentada seja [vueSimulationsVides]:

Image

Para exibir a vista acima, utilizaremos a seguinte página [simulationsVides.xhtml]:


<h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>

12.5.8. A ação [ removeSimulation]

O utilizador pode remover simulações da sua lista:

Image

O resultado é o seguinte:

Image

Se removemos a última simulação acima, obtemos o seguinte resultado:

Image

O código JSF para a coluna [Remover] na tabela de simulação é o seguinte:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <ui:composition template="layout.xhtml">
    <ui:define name="part1">
      <h2>Votre liste de simulations est vide.</h2>
    </ui:define>
  </ui:composition>
</html>
  • Linha 5: O link [Remove] está associado ao método [removeSimulation] da classe [Form]. Este método precisa de saber o número da simulação a ser removida. Esta informação é fornecida pela tag <f:setPropertyActionListener> na linha 8. Esta tag tem dois atributos, target e value: o atributo target especifica um campo no modelo ao qual o valor do atributo value será atribuído. Aqui, o número da simulação a ser removida, #{simulation.num}, será atribuído ao campo numSimulationToDelete da classe [Form]:

          <h:dataTable value="#{form.simulations}" var="simulation"
                       headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
...
            <h:column>
              <h:commandLink value="Retirer" action="#{form.retirerSimulation}">
                <f:setPropertyActionListener target="#{form.numSimulationToDelete}" value="#{simulation.num}"/>
              </h:commandLink>
            </h:column>
          </h:dataTable>

Quando o método [removeSimulation] da classe [Form] for executado, será possível utilizar o valor que foi previamente armazenado no campo numSimulationToDelete.


Pergunta: Escreva o método [retirerSimulation] da classe [Form].


12.5.9. A ação [ terminateSession]

O código JSF para o link [Fim da sessão] é o seguinte:


  // the view model
  ...
  private Integer numSimulationToDelete;

A ação [EndSession] associada ao link permite ao utilizador terminar a sua sessão e regressar ao formulário de entrada vazio:

Image

Image

Se o utilizador tivesse uma lista de simulações, esta é apagada. Além disso, a numeração das simulações é reiniciada para 1.


Pergunta: Escreva o método [endSession] para a classe [Form].


12.6. Integração da camada web numa arquitetura de três camadas JSF/EJB

A arquitetura da aplicação web anterior era a seguinte:

Substituímos a camada [de negócios] simulada pelas camadas [de negócios, DAO, JPA] implementadas por EJBs na Secção 7.1:


Exercício prático: Integre as camadas JSF e EJB seguindo a metodologia da Secção 11.