Skip to content

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

Voltamos aqui à arquitetura inicial, em que a camada [métier] era simulada. Sabemos agora que esta pode ser facilmente substituída pela camada real [métier]. A camada simulada [métier] facilita os testes.

Uma aplicação JSF é do tipo MVC (Modelo-Vista-Controlador):

  • o servlet [Faces Servlet] é o controlador genérico fornecido pelo JSF. Este controlador é estendido pelos gestores de eventos específicos da aplicação. Os gestores de eventos encontrados até agora eram métodos das classes que serviam de modelos para as páginas JSF
  • as páginas JSF enviam as respostas para o navegador do cliente. Estas são as vistas da aplicação.
  • As páginas JSF contêm elementos dinâmicos a que se chama modelo da página. Recorde-se que, para alguns autores, o modelo abrange as entidades manipuladas pela aplicação, tais como, por exemplo, as classes FeuilleSalaire ou Employe. Para distinguir estes dois modelos, pode-se falar de modelo da aplicação e modelo de uma 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 desencadeia um POST através de um evento qualquer [1]
  • em JSF, este POST será tratado, em geral, por um método C do modelo Mi da página JSFi. Pode dizer-se que o método C é um controlador secundário.
  • Se, no final deste método, a página JSFj tiver de ser apresentada, o controlador C deve:
  1. atualizar o modelo Mj da página JSFj com [2c]
  2. devolver ao controlador principal a chave de navegação [2a], que permitirá a exibição da página JSFj

A etapa 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. Com efeito, o gestor C do modelo Mi que atualiza o modelo Mj tem de conhecer este último. Se for necessário alterar o modelo Mj, será então necessário alterar o gestor C do modelo Mi.

Existe um caso em que a dependência entre os modelos pode ser evitada: aquele em que existe um único modelo M que serve para todas as páginas JSF. Esta arquitetura só é utilizável em aplicações com poucas vistas, mas revela-se então muito simples de utilizar. É essa que utilizamos 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 apresenta o formulário de simulação

  • a vista [VueSimulation], utilizada para apresentar o resultado detalhado da simulação:

Image

Image

  • a vista [VueSimulations], que apresenta a lista das simulações realizadas pelo cliente

Image

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

Image

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

Image

12.2. O projeto NetBeans da camada [web]

O projeto NetBeans desta 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 das vistas,
  • 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 analisar alguns destes elementos.

12.2.1. Os 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 pormenor no [web.xml]:


<?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>
  • 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:


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

Eis alguns exemplos de código JSF que utilizam estes estilos:

Vista Simulações


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

O resultado obtido é o seguinte:

Image

Vista de erro


          <h:dataTable value="#{form.erreurs}" var="erreur"
                                 headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">

Image

12.2.3. O ficheiro de mensagens

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


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

12.2.4. A camada [métier]

A camada [métier] simulada é alterada da seguinte forma:


package metier;

...
public class Metier implements ImetierLocal, Serializable {
  
  // lista de funcionários
  private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
  private List<Employe> listEmployes;
  
  // obter a folha de vencimento
  public FeuilleSalaire calculerFeuilleSalaire(String SS,
    double nbHeuresTravaillées, int nbJoursTravaillés) {
    // recuperar o funcionário
    Employe e=hashEmployes.get(SS);
    // lança uma exceção se o funcionário não existir
    if(e==null){
      throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
    }
    // retorna uma folha de vencimento fictícia
    return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),e.getIndemnite(),new ElementsSalaire(100,100,100,100,100));
  }
  
  // lista de funcionários
  public List<Employe> findAllEmployes() {
    if(listEmployes==null){
      // cria-se uma lista de três funcionários
      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)));
      // dicionário de funcionários
      for(Employe e:listEmployes){
        hashEmployes.put(e.getSS(),e);
      }
      // adiciona-se um funcionário que não existe no dicionário
      listEmployes.add(new Employe("X","Y","Z","La brûlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12)));
    }
    // retornar a lista de funcionários
    return listEmployes;
  }
}
  • linha 8: a lista de funcionários
  • linha 7: a mesma lista sob a forma de um dicionário indexado pelo n.º SS dos funcionários
  • linhas 24-39: o método findAllEmployes que devolve a lista de funcionários. Este método cria uma lista estática e faz referência ao campo employés 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 hashEmployes (linha 7). Isto para que ele apareça na lista suspensa de funcionários, mas que, posteriormente, não seja reconhecido pelo método calculerFeuilleSalaire (linha 14), de modo a que este lance uma exceção (linha 17).
  • linhas 11-21: o método calculerFeuilleSalaire
  • linha 14: o funcionário é procurado no dicionário hashEmployes através do seu n.º SS. Se não for encontrado, é lançada uma exceção (linhas 16-18). Assim, teremos uma exceção para o funcionário com o n.º SS X, adicionado na linha 35 à lista de funcionários, mas não no dicionário hashEmployes.
  • Na linha 20, é criada e devolvida uma folha de salário fictícia.

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á o âmbito application:


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 {

  // camada de negócio
  private IMetierLocal metier = new Metier();
  // registo
  private boolean logsEnabled = true;
  private final Logger logger = Logger.getLogger("pam");

  public ApplicationData() {
  }

  @PostConstruct
  public void init() {
    // registo
    if (isLogsEnabled()) {
      logger.info("ApplicationData");
    }
  }

  // getters e setters
...
}
  • linha 11: a anotação @Named torna a classe um bean gerido. Note-se que, ao contrário do projeto anterior, não foi utilizada a anotação @ManagedBean. A razão para tal é que a referência a esta classe deve ser injetada noutra classe através da anotação @Inject e que esta apenas injeta classes com a anotação @Named.
  • linha 12: a anotação @ApplicationScoped torna a classe um objeto de â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 funções:

  • linha 16: manter uma referência à camada [métier],
  • linhas 18-19: definir um registador que poderá ser utilizado pelos outros beans para registar mensagens na consola do Glassfish.

12.3.2. O bean SessionData

O bean SessionData terá um âmbito de sessã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 {

  // aplicação
  @Inject
  private ApplicationData applicationData;
  // simulações
  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;
  // configuração regional
  private String locale="fr_FR";
  
  // construtor
  public SessionData() {
  }

  @PostConstruct
  public void init() {
    // registo
    if (applicationData.isLogsEnabled()) {
      applicationData.getLogger().info("SessionData");
    }
  }

  // gestão de menus
  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 e setters
  ...
}
  • linha 13: a classe SessionData é um bean gerido (@Named) que poderá ser injetado noutros beans geridos,
  • linha 14: tem âmbito de sessão (@SessionScoped),
  • linhas 18-19: é-lhe injetada uma referência ao bean ApplicationData (@Inject),
  • linhas 21-32: os dados da aplicação que devem ser mantidos ao longo das sessões.
  • linha 21: a lista das simulações realizadas pelo utilizador,
  • linha 22: o número da última simulação registada,
  • linha 23: a última simulação realizada,
  • linhas 25-30: as opções do menu,
  • linha 32: a configuração regional da aplicação.

Linhas 39-44: o método init é executado após a instanciação da classe (@PostConstruct). Aqui, é utilizado apenas para registar a sua execução. Deve ser possível verificar que só é executado 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 esta razão que foi necessário injetar uma referência a este bean (linhas 18-19).

12.3.3. O bean Form

O bean Form tem o âmbito requête:


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() {
  }
  // outros beans
  @Inject
  private ApplicationData applicationData;
  @Inject
  private SessionData sessionData;
  // o modelo das vistas
  private String comboEmployesValue = "";
  private String heuresTravaillées = "";
  private String joursTravaillés = "";
  private Integer numSimulationToDelete;
  private List<Erreur> erreurs = new ArrayList<Erreur>();
  private FeuilleSalaire feuilleSalaire;

  
  // lista de funcionários
  public List<Employe> getEmployes(){
    return applicationData.getMetier().findAllEmployes();
  }
  
  // ação do menu
  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 e setters
...
}
  • na linha 17, a classe é um bean gerido (@Named),
  • na linha 18, de âmbito de requisição (@RequestScoped),
  • linhas 24-25: injeção de uma referência no bean com âmbito de aplicação ApplicationData,
  • linhas 26-27: injeção de uma referência ao bean com âmbito de sessão SessionData.

12.4. As páginas da aplicação

  

12.4.1. [layout.xhtml]

A página [layout.xhtml] assegura a formatação de todas as vistas:


<?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(){
        // alteramos os valores enviados
        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">
        <!-- cabeçalho -->
        <ui:include src="entete.xhtml" />
        <!-- conteúdo -->
        <ui:insert name="part1" >
          Gestion des assistantes maternelles
        </ui:insert>
        <ui:insert name="part2"/>
      </h:form>
    </h:body>
  </f:view>
</html>

Cada vista é constituída pelos seguintes elementos:

  • um cabeçalho apresentado pelo fragmento [entete.xhtml] (linha 24),
  • um corpo formado 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: um 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">

  <ui:composition>
    <!-- cabeçalho -->
    <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 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 do bean SessionData,
  • linha 17: um clique no link [Effacer la simulation] provoca a execução da função JavaScript raz. Esta foi definida no modelo [layout.xhtml],
  • linha 18: o atributo immediate=true faz com que a validade dos dados não seja verificada antes da execução do método [Form].enregistrerSimulation. Isto é intencional. Pode ser necessário registar a última simulação efetuada há um minuto, mesmo que os dados introduzidos desde então no formulário (mas ainda não validados) não sejam válidos. O mesmo se aplica às ações [Voir les simulations], [Effacer la simulation], [Retour au simulateur] e [Terminer la 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 template="layout.xhtml">
    <ui:define name="part1">
      <ui:include src="saisie2.xhtml"/>
    </ui:define>
  </ui:composition>
</html>
  • linha 8: é apresentada no interior da página [layout.xhtml],
  • linha 9: no lugar do fragmento denominado part1. Neste fragmento, é apresentada a página [saisie2.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">

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

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

 

Pergunta: complete a linha 13 do código XHTML. A lista de elementos da lista suspensa dos funcionários é fornecida por um método do bean [Form]. Escreva esse método. Os elementos apresentados no menu suspenso terão a sua propriedade itemValue igual ao n.º SS de um funcionário e a propriedade itemLabel será uma cadeia de caracteres formada pelo nome próprio e apelido desse funcionário.



Pergunta: como deve ser inicializado o bean [SessionData] para que, aquando da consulta inicial GET efetuada ao formulário, o menu do cabeçalho seja o apresentado acima?


12.5.2. A ação [faireSimulation]

O código JSF da ação [faireSimulation] é o seguinte:


<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>

A ação [faireSimulation] calcula uma folha de salários:

Image

A partir da página anterior, obtém-se o seguinte resultado:

A simulação é apresentada na seguinte página [simulation.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 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>
  • na linha 8, a página [simulation.xhtml] é inserida na página [layout.xhtml],
  • linhas 9-11: a área de introdução de dados é apresentada no fragmento «part1» da página de layout,
  • linhas 12-91: a simulação é apresentada no fragmento part2 da página de layout.

Questão: escreva o método [faireSimulation] da classe [Form]. A simulação será guardada no bean SessionData.


12.5.3. Gestão de erros

Pretende-se gerir adequadamente as exceções que possam ocorrer durante o cálculo de uma simulação. Para tal, o código do método [faireSimulation] utilizará um try/catch:


  // ação do menu
  public String faireSimulation(){
    try{
      // calcula-se a folha de pagamentos
      feuilleSalaire= ...
      // exibe-se a simulação
      ...
      // atualiza-se o menu
      ...
       // recupera-se a vista de simulação
      return "simulation";
    }catch(Throwable th){
      // esvazia-se a lista de erros anteriores
      ...
      // cria-se a nova lista de erros
      ...
      // exibe-se a vista vueErreur
      ...
      // atualiza-se o menu
      ...
       // exibe-se a vista de erros
      return "erreurs";
    }
}

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


  // o modelo das vistas
  ...
  private List<Erreur> erreurs=new ArrayList<Erreur>();
...

A classe Erro é definida da seguinte forma:


package web.entities;

public class Erreur {
  
  public Erreur() {
  }
  
  // campo
  private String classe;
  private String message;

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

Os erros serão exceções cujo nome da classe é armazenado no campo «classe» e cuja mensagem é armazenada no campo «mensagem».

A lista de erros criada no método [faireSimulation] é constituída por:

  • a exceção inicial th do tipo Throwable que ocorreu,
  • da sua causa th.getCause(), caso exista,
  • da causa da causa h.getCause().getCause(), caso exista.
  • ...

Eis um exemplo de lista de erros:

Image

No exemplo acima, o funcionário [Z Y] não existe no dicionário de funcionários utilizado pela camada simulada [métier]. Neste caso, a camada simulada [métier] lança uma exceção (linha 6 abaixo):


  public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
    // recupera-se o funcionário
    Employe e=hashEmployes.get(SS);
    // lança-se uma exceção se o funcionário não existir
    if(e==null){
      throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
    }
...
}

O resultado obtido é o seguinte:

Image

Esta vista é apresentada pela página [erreurs.xhtml] 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>

Questão: completar o método [faireSimulation] para que, em caso de exceção, este exiba a vista [vueErreur].


12.5.4. A ação [effacerSimulation]

A ação [effacerSimulation] permite ao utilizador aceder a um formulário em branco:

O código JSF do link [effacerSimulation] é o seguinte:


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

Ao clicar no link [EffacerSimulation], é primeiro chamada a função JavaScript raz(). Este método está definido na página [layout.xhtml]:


    <script type="text/javascript">
      function raz(){
        // alteramos os valores enviados
        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>

As linhas 4-6 alteram os valores introduzidos. Note-se que

  • os valores enviados são válidos, c.a.d, e que passarão nos testes de validação dos campos de introdução de dados heuresTravaillees e joursTravailles.
  • a função raz não envia o formulário. Com efeito, este será enviado através do link cmdEffacerSimulation. Este post será executado após a execução da função JavaScript raz.

Durante a execução da função post, os valores enviados seguirão um percurso normal: validação e, em seguida, atribuição aos campos do modelo. Estes são os seguintes na classe [Form]:


  // o modelo das vistas
  private String comboEmployesValue;
  private String heuresTravaillées;
  private String joursTravaillés;
  ...

Estes três campos irão receber os três valores lançados {"0","0","0"}. Assim que esta atribuição for efetuada, o método effacerSimulation será executado.


Pergunta: escreva o método [effacerSimulation] da classe [Form]. Deve garantir que:

  • apenas a área de introdução de dados seja apresentada,

  • a lista suspensa esteja posicionada no seu primeiro elemento,

  • os campos de entrada heuresTravaillees e joursTravailles apresentem cadeias de caracteres vazias.


12.5.5. A ação [enregistrerSimulation]

O código JSF da ligação [enregistrerSimulation] é o seguinte:


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

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


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

A classe « Simulação» é a seguinte:


package web.entities;

import metier.FeuilleSalaire;

public class Simulation {
  
  public Simulation() {
  }

  // campos de uma simulação
  private Integer num;
  private FeuilleSalaire feuilleSalaire;
  private String heuresTravaillées;
  private String joursTravaillés;
  
  // construtor
  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 e setters
...
}

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

  • linha 11: o n.º da simulação,
  • linha 12: a folha de salário que foi calculada,
  • linha 13: o número de horas trabalhadas,
  • linha 14: o número de dias trabalhados.

Eis um exemplo de registo:

Image

A partir da página anterior, obtém-se o seguinte resultado:

Image

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


  // simulações
  private List<Simulation> simulations = new ArrayList<Simulation>();
  private int numDerniereSimulation = 0;
private Simulation simulation;
  • linha 2: o número da última simulação realizada.

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

  • recuperar o número da última simulação no 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ções é apresentada pela página [simulations.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 template="layout.xhtml">
    <ui:define name="part1">
      <!-- tabela de simulações -->
      <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>
  • na linha 8, a página [simulations.xhtml] é inserida na página [layout.xhtml],
  • na linha 9, no lugar do fragmento denominado part1,
  • na linha 11, a baliza <h:dataTable> utiliza o campo #{sessionData.simulations} como fonte de dados, c.a.d. O campo seguinte:

  // simulações
private List<Simulation> simulations;
  • o atributo var="simulation" define o nome da variável que representa a simulação atual dentro da baliza <h:datatable>

  • o atributo headerClass="simulationsHeaders" define o estilo dos títulos das colunas da tabela.

  • o atributo columnClasses="...." define o estilo de cada uma das colunas da tabela

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

O código JSF da coluna Nom é o seguinte:


            <h:column>
              <f:facet name="header">
                <h:outputText value="#{msg['simulations.headers.nom']}"/>
              </f:facet>
              <h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
</h:column>
  • linhas 2-4: a baliza <f:facet name="header"> define o título da coluna
  • linha 5: o nome do funcionário é escrito:
    • a simulação faz referência ao atributo var da baliza <h:dataTable ...>:

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

«simulação» designa a simulação atual da lista de simulações: primeiro a 1.ª, depois a 2.ª, …

  • (continuação)
    • simulation.feuilleSalaire refere-se ao campo feuilleSalaire da simulação atual
    • simulation.feuilleSalaire.employe refere-se ao campo «empregado» do campo feuilleSalaire
    • simulation.feuilleSalaire.employe.nom refere-se ao campo «nome» do campo «empregado»

A mesma técnica é repetida para todas as colunas da tabela. Existe uma dificuldade na coluna «Indemnizações», que é gerada com o seguinte código:


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

Na linha 5, é apresentado o valor de simulation.indemnites. No entanto, a classe «Simulation» não possui o campo «indemnites». É importante lembrar que o campo «indemnites» não é utilizado diretamente, mas sim através do método simulation.getIndemnites(). Basta, portanto, que esse método exista. O campo indemnites, por sua vez, pode não existir. O método getIndemnites deve calcular o total das indemnizações do empregado. Isto requer um cálculo intermédio, uma vez que esse total não está diretamente disponível na folha de salário. O método getIndemnites é apresentado no parágrafo 12.5.5.


Questão: escreva o método [enregistrerSimulation] da classe [Form].


12.5.6. A ação [retourSimulateur]

O código JSF da ligação [retourSimulateur] é o seguinte:


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

A ação [retourSimulateur] associada ao link permite ao utilizador regressar da vista [vueSimulations] à vista [vueSaisies]:

Image

O resultado obtido:

Image


Questão: escreva o método [retourSimulateur] da classe [Form]. O formulário de introdução de dados apresentado deve estar vazio, tal como acima.


12.5.7. A ação [voirSimulations]

O código JSF do link [voirSimulations] é o seguinte:


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

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

Image

O resultado obtido:

Image


Questão: escreva o método [voirSimulations] da classe [Form].


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

Image

Para apresentar a vista acima, utilizar-se-á a seguinte página [simulationsVides.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 template="layout.xhtml">
    <ui:define name="part1">
      <h2>Votre liste de simulations est vide.</h2>
    </ui:define>
  </ui:composition>
</html>

12.5.8. A ação [retirerSimulation]

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

Image

O resultado obtido é o seguinte:

Image

Se, no exemplo acima, se remover a última simulação, obter-se-á o seguinte resultado:

Image

O código JSF da coluna [Retirer] da tabela de simulações é o seguinte:


          <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>
  • linha 5: o link [Retirer] está associado ao método [retirerSimulation] da classe [Form]. Este método necessita de saber o número da simulação a remover. Este número é-lhe fornecido pela baliza <f:setPropertyActionListener> da linha 8. Esta baliza tem dois atributos: «target» e «value»; o atributo «target» designa um campo do modelo ao qual será atribuído o valor do atributo «value». Neste caso, o número da simulação a remover #{simulation.num} será atribuído ao campo numSimulationToDelete da classe [Form]:

  // modelo das vistas
  ...
  private Integer numSimulationToDelete;

Quando o método [retirerSimulation] da classe [Form] for executado, poderá utilizar o valor que tiver sido previamente armazenado no campo numSimulationToDelete.


Tarefa: escrever o método [retirerSimulation] da classe [Form].


12.5.9. A ação [terminerSession]

O código JSF do link [Terminer la session] é o seguinte:


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

A ação [terminerSession] associada ao link permite ao utilizador encerrar a sua sessão e regressar ao formulário de preenchimento em branco:

Image

Image

Se o utilizador tivesse uma lista de simulações, esta é esvaziada. Além disso, a numeração das simulações recomeça a partir de 1.


Questão: escreva o método [terminerSession] da classe [Form].


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

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

Substituímos a camada [métier] simulada pelas camadas [métier, DAO, jpa] implementadas por EJB no parágrafo 7.1:


Trabalho prático: realizar a integração das camadas JSF e EJB seguindo a metodologia do parágrafo 11.