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:
- atualizar [2c] o modelo Mj da página JSFj
- 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:


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

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

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

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:

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

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:

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:

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:

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:

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

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

O resultado obtido:

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:

O resultado obtido:

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

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:

O resultado é o seguinte:

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

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:


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.
![]() |














