12. Versione 7 - Applicazione web PAM multi-vista / multi-pagina
Torniamo qui all’architettura iniziale in cui il livello [métier] era simulato. Ora sappiamo che questo può essere facilmente sostituito dal livello [métier] reale. Il livello [métier] simulato facilita i test.
![]() |
Un'applicazione JSF è di tipo MVC (Modello-Vista-Controllore):
- il servlet [Faces Servlet] è il controller generico fornito da JSF. Questo controller viene esteso dai gestori di eventi specifici dell'applicazione. I gestori di eventi incontrati finora erano metodi delle classi che fungono da modelli per le pagine JSF
- le pagine JSF inviano le risposte al browser client. Queste sono le viste dell'applicazione.
- le pagine JSF contengono elementi dinamici denominati modello della pagina. Si ricorda che, secondo alcuni autori, il modello comprende le entità gestite dall’applicazione, come ad esempio le classi FeuilleSalaire o Employe. Per distinguere questi due modelli, si può parlare di modello dell’applicazione e di modello di una pagina JSF.
Nell’architettura JSF, il passaggio da una pagina JSFi a una pagina JSFj può risultare problematico.
- È stata visualizzata la pagina JSFi. Da questa pagina, l’utente attiva un POST tramite un evento qualsiasi [1]
- in JSF; tale POST verrà generalmente elaborato [2a,2b] da un metodo C del modello Mi della pagina JSFi. Si può affermare che il metodo C sia un controller secondario.
- Se al termine di questo metodo deve essere visualizzata la pagina JSFj, il controller C deve:
- aggiornare il modello Mj della pagina JSFj con [2c]
- restituire al controller principale [2a] la chiave di navigazione che consentirà la visualizzazione della pagina JSFj
La fase 1 richiede che il modello Mi della pagina JSFi contenga un riferimento al modello Mj della pagina JSFj. Ciò complica leggermente le cose, rendendo i modelli Mi interdipendenti. Infatti, il gestore C del modello Mi che aggiorna il modello Mj deve conoscerlo. Se si dovesse modificare il modello Mj, si dovrebbe quindi modificare anche il gestore C del modello Mi.
Esiste un caso in cui è possibile evitare la dipendenza reciproca dei modelli: quello in cui esiste un unico modello M che serve a tutte le pagine JSF. Questa architettura è utilizzabile solo nelle applicazioni che hanno poche viste, ma in tal caso risulta molto semplice da usare. È quella che utilizziamo attualmente.
In questo contesto, l’architettura dell’applicazione è la seguente:
![]() |
12.1. Le viste dell’applicazione
Le diverse viste presentate all’utente saranno le seguenti:
-
la vista [VueSaisies] che presenta il modulo di simulazione
-
la vista [VueSimulation] utilizzata per visualizzare il risultato dettagliato della simulazione:


- la vista [VueSimulations] che fornisce l'elenco delle simulazioni effettuate dal cliente

- la vista [VueSimulationsVides] che indica che il cliente non ha o non ha più simulazioni:

- la vista [VueErreur] che segnala uno o più errori:

12.2. Il progetto NetBeans del livello [web]
Il progetto NetBeans di questa versione sarà il seguente progetto Maven:
![]() |
- in [1], i file di configurazione,
- in [2], le pagine JSF,
- in [3], il foglio di stile e l'immagine di sfondo delle viste,
- in [4], le classi del livello [web],
- in [5], i livelli inferiori dell'applicazione,
- in [6], il file dei messaggi per l'internazionalizzazione dell'applicazione,
- in [7], le dipendenze del progetto.
Esaminiamo alcuni di questi elementi.
12.2.1. I file di configurazione
I file [web.xml] e [faces-config.xml] sono identici a quelli del progetto precedente, ad eccezione di un dettaglio presente in [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>
- righe 4-6: la pagina iniziale è la pagina [saisie.xhtml]
12.2.2. Il foglio di stile
Il file [styles.css] è il seguente:
.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
}
Ecco alcuni esempi di codice JSF che utilizzano questi stili:
Vista Simulazioni
<h:dataTable value="#{form.simulations}" var="simulation"
headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
Il risultato ottenuto è il seguente:

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

12.2.3. Il file dei messaggi
Il file dei messaggi [messages_fr.properties] è il seguente:
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. Il livello [métier]
Il livello simulato [métier] viene modificato come segue:
package metier;
...
public class Metier implements ImetierLocal, Serializable {
// elenco dei dipendenti
private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
private List<Employe> listEmployes;
// ottenere la busta paga
public FeuilleSalaire calculerFeuilleSalaire(String SS,
double nbHeuresTravaillées, int nbJoursTravaillés) {
// recupera il dipendente
Employe e=hashEmployes.get(SS);
// viene generata un'eccezione se il dipendente non esiste
if(e==null){
throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
}
// si restituisce una busta paga fittizia
return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),e.getIndemnite(),new ElementsSalaire(100,100,100,100,100));
}
// elenco dei dipendenti
public List<Employe> findAllEmployes() {
if(listEmployes==null){
// si crea un elenco di tre dipendenti
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)));
// dizionario dei dipendenti
for(Employe e:listEmployes){
hashEmployes.put(e.getSS(),e);
}
// si aggiunge un dipendente che non esiste nel dizionario
listEmployes.add(new Employe("X","Y","Z","La brûlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12)));
}
// si restituisce l'elenco dei dipendenti
return listEmployes;
}
}
- riga 8: l’elenco dei dipendenti
- riga 7: lo stesso elenco sotto forma di dizionario indicizzato in base al n. SS dei dipendenti
- righe 24-39: il metodo findAllEmployes che restituisce l'elenco dei dipendenti. Questo metodo crea un elenco statico e fa riferimento al campo employés della riga 8.
- righe 27-33: vengono creati un elenco e un dizionario contenenti due dipendenti
- riga 35: un dipendente viene aggiunto all’elenco dei dipendenti (riga 8) ma non al dizionario hashEmployes (riga 7). Questo affinché appaia nel menu a tendina dei dipendenti ma non venga poi riconosciuto dal metodo calculerFeuilleSalaire (riga 14), in modo che quest’ultimo generi un’eccezione (riga 17).
- righe 11-21: il metodo calculerFeuilleSalaire
- riga 14: il dipendente viene cercato nel dizionario hashEmployes tramite il suo n. SS. Se non viene trovato, viene generata un'eccezione (righe 16-18). Si avrà quindi un'eccezione per il dipendente con n. SS X, aggiunto alla riga 35 nell'elenco dei dipendenti ma non nel dizionario hashEmployes.
- Alla riga 20 viene creato e restituito un foglio paga fittizio.
12.3. I bean dell'applicazione
Ci saranno tre bean con tre ambiti di validità diversi:
![]() |
12.3.1. Il bean ApplicationData
Il bean ApplicationData avrà come bean di riferimento 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 {
// livello applicativo
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");
}
}
// getter e setter
...
}
- riga 11: l'annotazione @Named rende la classe un bean gestito. Si noti che, a differenza del progetto precedente, non è stata utilizzata l'annotazione @ManagedBean. Il motivo è che il riferimento a questa classe deve essere iniettato in un'altra classe tramite l'annotazione @Inject e che quest'ultima inietta solo classi con l'annotazione @Named.
- riga 12: l’annotazione @ApplicationScoped rende la classe un oggetto con ambito applicativo. Si noti che la classe dell’annotazione è [javax.enterprise.context.ApplicationScoped] (riga 6) e non [javax.faces.bean.ApplicationScoped] come nei bean del progetto precedente.
Il bean ApplicationData ha due funzioni:
- riga 16: mantenere un riferimento al livello [métier],
- righe 18-19: definire un logger che potrà essere utilizzato dagli altri bean per registrare i log sulla console di Glassfish.
12.3.2. Il bean SessionData
Il bean SessionData avrà un ambito di sessione:
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 {
// applicazione
@Inject
private ApplicationData applicationData;
// simulazioni
private List<Simulation> simulations = new ArrayList<Simulation>();
private int numDerniereSimulation = 0;
private Simulation simulation;
// menu
private boolean menuFaireSimulationIsRendered = true;
private boolean menuEffacerSimulationIsRendered = true;
private boolean menuEnregistrerSimulationIsRendered;
private boolean menuVoirSimulationsIsRendered;
private boolean menuRetourSimulateurIsRendered;
private boolean menuTerminerSessionIsRendered = true;
// locale
private String locale="fr_FR";
// costruttore
public SessionData() {
}
@PostConstruct
public void init() {
// registro
if (applicationData.isLogsEnabled()) {
applicationData.getLogger().info("SessionData");
}
}
// gestione dei menu
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);
}
// getter e setter
...
}
- riga 13: la classe SessionData è un bean gestito (@Named) che potrà essere iniettato in altri bean gestiti,
- riga 14: ha ambito di sessione (@SessionScoped),
- righe 18-19: gli viene iniettato un riferimento al bean ApplicationData (@Inject),
- righe 21-32: i dati dell'applicazione che devono essere mantenuti tra una sessione e l'altra.
- riga 21: l'elenco delle simulazioni effettuate dall'utente,
- riga 22: il numero dell'ultima simulazione salvata,
- riga 23: l'ultima simulazione effettuata,
- righe 25-30: le opzioni del menu,
- riga 32: le impostazioni locali dell'applicazione.
Righe 39-44: il metodo init viene eseguito dopo l'istanziazione della classe (@PostConstruct). In questo caso, viene utilizzato solo per lasciare traccia della sua esecuzione. Dovremmo poter verificare che venga eseguito una sola volta per utente, poiché la classe ha ambito di sessione. Riga 42: il metodo utilizza il logger definito nella classe ApplicationData. È per questo motivo che era necessario iniettare un riferimento a questo bean (righe 18-19).
12.3.3. Il bean Form
Il bean Form ha ambito 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() {
}
// altri bean
@Inject
private ApplicationData applicationData;
@Inject
private SessionData sessionData;
// il modello delle viste
private String comboEmployesValue = "";
private String heuresTravaillées = "";
private String joursTravaillés = "";
private Integer numSimulationToDelete;
private List<Erreur> erreurs = new ArrayList<Erreur>();
private FeuilleSalaire feuilleSalaire;
// elenco dei dipendenti
public List<Employe> getEmployes(){
return applicationData.getMetier().findAllEmployes();
}
// azione del 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() {
...
}
// getter e setter
...
}
- riga 17, la classe è un bean gestito (@Named),
- riga 18, con ambito di richiesta (@RequestScoped),
- righe 24-25: iniezione di un riferimento al bean con ambito applicativo ApplicationData,
- righe 26-27: iniezione di un riferimento al bean con ambito di sessione SessionData.
12.4. Le pagine dell'applicazione
![]() |
12.4.1. [layout.xhtml]
La pagina [layout.xhtml] gestisce l’impaginazione di tutte le viste:
<?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(){
// modifica dei valori inviati
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">
<!-- intestazione -->
<ui:include src="entete.xhtml" />
<!-- contenuto -->
<ui:insert name="part1" >
Gestion des assistantes maternelles
</ui:insert>
<ui:insert name="part2"/>
</h:form>
</h:body>
</f:view>
</html>
Ogni vista è costituita dai seguenti elementi:
- un'intestazione visualizzata dal frammento [entete.xhtml] (riga 24),
- un corpo formato da due frammenti denominati part1 (righe 26-28) e part2 (riga 29),
- riga 8: l'attributo locale garantisce l'internazionalizzazione delle pagine. In questo caso ce ne sarà solo uno: fr_FR,
- righe 14-19: un codice JavaScript.
La visualizzazione della pagina [layout.xhtml] è la seguente:
![]() |
- in [1], l'intestazione [entete.xhtml],
- in [2], il frammento part1.
12.4.2. L'intestazione [entete.xhtml]
Il codice della pagina [entete.xhtml] è il seguente:
<?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>
<!-- intestazione -->
<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>
- riga 12: il titolo dell'applicazione,
- righe 16-21: i sei link corrispondenti alle sei azioni che l’utente può eseguire. Questi link sono controllati (attributo rendered) da variabili booleane del bean SessionData,
- riga 17: un clic sul link [Effacer la simulation] provoca l’esecuzione della funzione JavaScript raz. Questa è stata definita nel modello [layout.xhtml],
- riga 18: l’attributo immediate=true fa sì che la validità dei dati non venga verificata prima dell’esecuzione del metodo [Form].enregistrerSimulation. È intenzionale. Si potrebbe voler salvare l’ultima simulazione effettuata un minuto fa, anche se i dati inseriti successivamente nel modulo (ma non ancora convalidati) non sono validi. Lo stesso vale per le azioni [Voir les simulations], [Effacer la simulation], [Retour au simulateur] e [Terminer la session].
12.5. Casi d’uso dell’applicazione
12.5.1. Visualizzazione della pagina iniziale
La pagina iniziale è la seguente pagina [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>
- riga 8: viene visualizzata all’interno della pagina [layout.xhtml],
- riga 9: al posto del frammento denominato part1. In questo frammento viene visualizzata la pagina [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>
Questo codice visualizza la seguente vista:
![]() |
Domanda: completare la riga 13 del codice XHTML. L'elenco degli elementi del menu a tendina dei dipendenti è fornito da un metodo del bean [Form]. Scrivere tale metodo. Gli elementi visualizzati nel menu a tendina avranno la proprietà itemValue pari al numero SS di un dipendente e la proprietà itemLabel sarà una stringa composta dal nome e dal cognome dello stesso.
Domanda: come deve essere inizializzato il bean [SessionData] affinché, al momento della richiesta iniziale GET effettuata al modulo, il menu dell’intestazione sia quello mostrato sopra?
12.5.2. L'azione [faireSimulation]
Il codice JSF dell'azione [faireSimulation] è il seguente:
<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>
L'azione [faireSimulation] calcola una busta paga:

Dalla pagina precedente si ottiene il seguente risultato:
![]() |
![]() |
La simulazione viene visualizzata nella pagina successiva [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>
- riga 8, la pagina [simulation.xhtml] viene inserita nella pagina [layout.xhtml],
- righe 9-11: l'area di immissione dati viene visualizzata nel frammento part1 della pagina di layout,
- righe 12-91: la simulazione viene visualizzata nel frammento part2 della pagina di layout.
Domanda: scrivere il metodo [faireSimulation] della classe [Form]. La simulazione verrà salvata nel bean SessionData.
12.5.3. Gestione degli errori
Si desidera poter gestire correttamente le eccezioni che possono verificarsi durante il calcolo di una simulazione. A tal fine, il codice del metodo [faireSimulation] utilizzerà un try / catch:
// azione dal menu
public String faireSimulation(){
try{
// si calcola la busta paga
feuilleSalaire= ...
// visualizzazione della simulazione
...
// si aggiorna il menu
...
// si torna alla vista di simulazione
return "simulation";
}catch(Throwable th){
// si svuota l'elenco degli errori precedenti
...
// si crea il nuovo elenco degli errori
...
// si visualizza la vista vueErreur
...
// si aggiorna il menu
...
// si visualizza la vista degli errori
return "erreurs";
}
}
L'elenco degli errori creato alla riga 15 è il seguente:
// il modello delle viste
...
private List<Erreur> erreurs=new ArrayList<Erreur>();
...
La classe Errore è definita come segue:
package web.entities;
public class Erreur {
public Erreur() {
}
// campo
private String classe;
private String message;
// costruttore
public Erreur(String classe, String message){
this.setClasse(classe);
this.message=message;
}
// getter e setter
...
}
Gli errori saranno eccezioni di cui si memorizza il nome della classe nel campo classe e il messaggio nel campo messaggio.
L'elenco degli errori generato nel metodo [faireSimulation] è costituito da:
- l'eccezione iniziale th di tipo Throwable che si è verificata,
- della sua causa th.getCause(), se ne ha una,
- della causa della causa h.getCause().getCause(), se esiste.
- ...
Ecco un esempio di elenco di errori:

Nell’esempio sopra riportato, il dipendente [Z Y] non esiste nel dizionario dei dipendenti utilizzato dal livello simulato [métier]. In questo caso, il livello simulato [métier] genera un’eccezione (riga 6 qui sotto):
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
// si recupera il dipendente
Employe e=hashEmployes.get(SS);
// si genera un'eccezione se il dipendente non esiste
if(e==null){
throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
}
...
}
Il risultato ottenuto è il seguente:

Questa vista viene visualizzata dalla pagina [erreurs.xhtml] seguente:
<?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>
Domanda: completare il metodo [faireSimulation] in modo che, in caso di eccezione, venga visualizzata la vista [vueErreur].
12.5.4. L'azione [effacerSimulation]
L'azione [effacerSimulation] consente all'utente di visualizzare un modulo vuoto:
![]() |
Il codice JSF del collegamento [effacerSimulation] è il seguente:
<h:commandLink id="cmdEffacerSimulation" onclick="raz()" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>
Cliccando sul link [EffacerSimulation] viene innanzitutto richiamata la funzione JavaScript raz(). Questo metodo è definito nella pagina [layout.xhtml]:
<script type="text/javascript">
function raz(){
// si modificano i valori inviati
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>
Le righe 4-6 modificano i valori inviati. Si noti che
- i valori inviati sono validi, c.a.d, e che supereranno i controlli di validità dei campi di immissione heuresTravaillees e joursTravailles.
- la funzione raz non invia il modulo. Infatti, questo verrà inviato tramite il collegamento cmdEffacerSimulation. Questo post avverrà dopo l’esecuzione della funzione JavaScript raz.
Durante l’esecuzione di post, i valori inviati seguiranno il normale percorso: convalida e successiva assegnazione ai campi del modello. Questi sono i seguenti nella classe [Form]:
// il modello delle viste
private String comboEmployesValue;
private String heuresTravaillées;
private String joursTravaillés;
...
Questi tre campi riceveranno i tre valori inviati {"0","0","0"}. Una volta effettuata questa assegnazione, verrà eseguito il metodo effacerSimulation.
Domanda: scrivere il metodo [effacerSimulation] della classe [Form]. Si dovrà fare in modo che:
-
venga visualizzata solo l'area di immissione dati,
-
il menu a tendina sia posizionato sul primo elemento,
-
i campi di immissione heuresTravaillees e joursTravailles visualizzino stringhe vuote.
12.5.5. L'azione [enregistrerSimulation]
Il codice JSF del collegamento [enregistrerSimulation] è il seguente:
<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}" rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>
L'azione [enregistrerSimulation] associata al collegamento consente di salvare la simulazione corrente in un elenco di simulazioni gestito nella classe [SessionData]:
private List<Simulation> simulations=new ArrayList<Simulation>();
La classe « : Simulazione» è la seguente:
package web.entities;
import metier.FeuilleSalaire;
public class Simulation {
public Simulation() {
}
// campi di una simulazione
private Integer num;
private FeuilleSalaire feuilleSalaire;
private String heuresTravaillées;
private String joursTravaillés;
// costruttore
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();
}
// getter e setter
...
}
Questa classe consente di memorizzare una simulazione effettuata dall'utente:
- riga 11: il numero della simulazione,
- riga 12: il foglio paga che è stato calcolato,
- riga 13: il numero di ore lavorate,
- riga 14: il numero di giorni lavorati.
Ecco un esempio di registrazione:

Partendo dalla pagina precedente, si ottiene il seguente risultato:

Il numero della simulazione è un numero che aumenta ad ogni nuova registrazione. Appartiene al bean SessionData:
// simulazioni
private List<Simulation> simulations = new ArrayList<Simulation>();
private int numDerniereSimulation = 0;
private Simulation simulation;
- riga 2: il numero dell'ultima simulazione effettuata.
Il metodo [enregistrerSimulation] può procedere come segue:
- recuperare il numero dell'ultima simulazione dal bean [SessionData] e incrementarlo,
- aggiungere la nuova simulazione all'elenco delle simulazioni gestito dalla classe [SessionData],
- visualizzare la tabella delle simulazioni:
![]() |
La tabella delle simulazioni viene visualizzata dalla pagina [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">
<!-- tabella delle simulazioni -->
<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>
- alla riga 8, la pagina [simulations.xhtml] viene inserita all'interno della pagina [layout.xhtml],
- riga 9, al posto del frammento denominato part1,
- riga 11, il tag <h:dataTable> utilizza il campo #{sessionData.simulations} come fonte di dati, c.a.d. Il campo successivo:
// simulazioni
private List<Simulation> simulations;
-
l'attributo var="simulation" imposta il nome della variabile che rappresenta la simulazione corrente all'interno del tag <h:datatable>
-
l'attributo headerClass="simulationsHeaders" definisce lo stile delle intestazioni delle colonne della tabella.
-
l'attributo columnClasses="...." definisce lo stile di ciascuna colonna della tabella
Esaminiamo una delle colonne della tabella e vediamo come è strutturata:
![]() |
Il codice JSF della colonna Nom è il seguente:
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.nom']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
</h:column>
- righe 2-4: il tag <f:facet name="header"> definisce il titolo della colonna
- riga 5: il nome del dipendente è scritto come segue:
- simulation fa riferimento all'attributo var del tag <h:dataTable ...>:
<h:dataTable value="#{sessionData.simulations}" var="simulation" ...>
«simulazione» indica la simulazione corrente nell’elenco delle simulazioni: prima la prima, poi la seconda, …
- (continua)
- simulation.feuilleSalaire fa riferimento al campo feuilleSalaire della simulazione corrente
- simulation.feuilleSalaire.employe fa riferimento al campo «dipendente» del campo feuilleSalaire
- simulation.feuilleSalaire.employe.nom fa riferimento al campo «nome» del campo «dipendente»
La stessa procedura viene ripetuta per tutte le colonne della tabella. Si presenta una difficoltà per la colonna «Indennità», che viene generata con il seguente codice:
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.indemnites']}"/>
</f:facet>
<h:outputText value="#{simulation.indemnites}"/>
</h:column>
Riga 5: viene visualizzato il valore di simulation.indemnites. Tuttavia, la classe Simulation non dispone di un campo "indemnites". È importante ricordare che il campo "indemnites" non viene utilizzato direttamente, ma tramite il metodo simulation.getIndemnites(). È quindi sufficiente che tale metodo esista. Il campo indemnites, invece, può non esistere. Il metodo getIndemnites deve restituire il totale delle indennità del dipendente. Ciò richiede un calcolo intermedio poiché tale totale non è disponibile direttamente nel foglio paga. Il metodo getIndemnites è riportato nel paragrafo 12.5.5.
Domanda: scrivere il metodo [enregistrerSimulation] della classe [Form].
12.5.6. L'azione [retourSimulateur]
Il codice JSF del collegamento [retourSimulateur] è il seguente:
<h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>
L'azione [retourSimulateur] associata al link consente all'utente di tornare dalla vista [vueSimulations] alla vista [vueSaisies]:

Risultato ottenuto:

Domanda: scrivere il metodo [retourSimulateur] della classe [Form]. Il modulo di immissione presentato deve essere vuoto come sopra.
12.5.7. L'azione [voirSimulations]
Il codice JSF del collegamento [voirSimulations] è il seguente:
<h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>
L'azione [voirSimulations] associata al link consente all'utente di visualizzare la tabella delle simulazioni, indipendentemente dallo stato dei dati inseriti:

Il risultato ottenuto:

Domanda: scrivere il metodo [voirSimulations] della classe [Form].
Si farà in modo che, se l'elenco delle simulazioni è vuoto, la vista visualizzata sia [vueSimulationsVides]:

Per visualizzare la vista sopra indicata, si utilizzerà la seguente pagina [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. L'azione [retirerSimulation]
L'utente può rimuovere le simulazioni dal proprio elenco:

Il risultato ottenuto è il seguente:

Se dall'elenco sopra riportato si rimuove l'ultima simulazione, si otterrà il seguente risultato:

Il codice JSF della colonna [Retirer] della tabella delle simulazioni è il seguente:
<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>
- riga 5: il collegamento [Retirer] è associato al metodo [retirerSimulation] della classe [Form]. Questo metodo necessita di conoscere il numero della simulazione da rimuovere. Tale numero gli viene fornito dal tag <f:setPropertyActionListener> della riga 8. Questo tag presenta due attributi: target e value; l’attributo target indica un campo del modello a cui verrà assegnato il valore dell’attributo value. In questo caso, il numero della simulazione da rimuovere #{simulation.num} verrà assegnato al campo numSimulationToDelete della classe [Form]:
// modello delle viste
...
private Integer numSimulationToDelete;
Quando verrà eseguito il metodo [retirerSimulation] della classe [Form], potrà utilizzare il valore precedentemente memorizzato nel campo numSimulationToDelete.
Domanda: scrivere il metodo [retirerSimulation] della classe [Form].
12.5.9. L'azione [terminerSession]
Il codice JSF del collegamento [Terminer la session] è il seguente:
<h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['form.menu.terminerSession']}" action="#{form.terminerSession}" rendered="#{sessionData.menuTerminerSessionIsRendered}"/>
L'azione [terminerSession] associata al link consente all'utente di chiudere la sessione e tornare al modulo di inserimento dati vuoto:


Se l'utente disponeva di un elenco di simulazioni, questo viene svuotato. Inoltre, la numerazione delle simulazioni riparte da 1.
Domanda: scrivere il metodo [terminerSession] della classe [Form].
12.6. Integrazione del livello web in un'architettura a tre livelli JSF / EJB
L'architettura dell'applicazione web precedente era la seguente:
![]() |
Sostituiamo il livello [métier] simulato con i livelli [métier, DAO, jpa] implementati tramite EJB nel paragrafo 7.1:
![]() |
Esercitazione pratica: integrare i livelli JSF e EJB seguendo la metodologia descritta nel paragrafo 11.
![]() |














