12. Version 7 – PAM-Webanwendung mit mehreren Ansichten/Seiten
Hier kehren wir zur ursprünglichen Architektur zurück, in der die [Business]-Schicht simuliert wurde. Wir wissen nun, dass diese problemlos durch die tatsächliche [Business]-Schicht ersetzt werden kann. Die simulierte [Business]-Schicht erleichtert das Testen.
![]() |
Eine JSF-Anwendung ist vom Typ MVC (Model-View-Controller):
- Das [Faces Servlet] ist der generische Controller, der von JSF bereitgestellt wird. Dieser Controller wird durch anwendungsspezifische Ereignisbehandler erweitert. Die bisher behandelten Ereignisbehandler waren Methoden von Klassen, die als Modelle für JSF-Seiten dienen
- JSF-Seiten senden Antworten an den Client-Browser. Dies sind die Views der Anwendung.
- JSF-Seiten enthalten dynamische Elemente, die als Seitenmodell bezeichnet werden. Beachten Sie, dass für manche Autoren das Modell die von der Anwendung manipulierten Entitäten umfasst, wie beispielsweise die Klassen `PayrollSheet` oder `Employee`. Um zwischen diesen beiden Modellen zu unterscheiden, können wir vom Anwendungsmodell und vom JSF-Seitenmodell sprechen.
In der JSF-Architektur kann der Übergang von einer JSFi-Seite zu einer JSFj-Seite problematisch sein.
- Die JSFi-Seite wurde angezeigt. Von dieser Seite aus löst der Benutzer über ein Ereignis einen POST-Request aus [1]
- In JSF wird diese POST-Anfrage in der Regel [2a,2b] von einer Methode C des Mi-Modells der JSFi-Seite verarbeitet. Man kann sagen, dass Methode C ein sekundärer Controller ist.
- Wenn am Ende dieser Methode die JSFj-Seite angezeigt werden soll, muss der Controller C:
- das Mj-Modell der JSFj-Seite aktualisieren [2c]
- dem Hauptcontroller den Navigationsschlüssel zurückgeben [2a], der die Anzeige der JSFj-Seite ermöglicht
Schritt 1 erfordert, dass das Mi-Modell der JSFi-Seite einen Verweis auf das Mj-Modell der JSFj-Seite enthält. Dies verkompliziert die Sache etwas, da die Mi-Modelle dadurch voneinander abhängig werden. Tatsächlich muss der C-Manager des Mi-Modells, der das Mj-Modell aktualisiert, dieses kennen. Wenn das Mj-Modell geändert werden muss, muss auch der C-Manager des Mi-Modells geändert werden.
Es gibt ein Szenario, in dem Modellabhängigkeiten vermieden werden können: wenn es ein einziges M-Modell gibt, das von allen JSF-Seiten verwendet wird. Diese Architektur eignet sich nur für Anwendungen mit wenigen Ansichten, ist aber sehr einfach zu verwenden. Dies ist die Architektur, die wir derzeit verwenden.
In diesem Zusammenhang sieht die Anwendungsarchitektur wie folgt aus:
![]() |
12.1. Die Anwendungsansichten
Dem Benutzer werden folgende Ansichten angezeigt:
-
die Ansicht [VueSaisies], die das Simulationsformular anzeigt
-
die Ansicht [VueSimulation], die zur Anzeige der detaillierten Simulationsergebnisse dient:


- die Ansicht [SimulationsView], in der die vom Kunden durchgeführten Simulationen aufgelistet werden

- die Ansicht [EmptySimulationsView], die anzeigt, dass der Client keine Simulationen oder keine weiteren Simulationen mehr hat:

- die Ansicht [ErrorView], die einen oder mehrere Fehler anzeigt:

12.2. Das NetBeans-Projekt für die [Web]-Schicht
Das NetBeans-Projekt für diese Version ist das folgende Maven-Projekt:
![]() |
- in [1], die Konfigurationsdateien,
- in [2] die JSF-Seiten,
- in [3] das Stylesheet und das Hintergrundbild für die Ansichten,
- in [4] die [Web]-Layer-Klassen,
- in [5] die unteren Schichten der Anwendung,
- in [6] die Meldungsdatei für die Internationalisierung der Anwendung,
- in [7] die Projektabhängigkeiten.
Sehen wir uns einige dieser Elemente genauer an.
12.2.1. Konfigurationsdateien
Die Dateien [web.xml] und [faces-config.xml] sind identisch mit denen im vorherigen Projekt, mit Ausnahme eines Details in [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>
- Zeilen 4–6: Die Startseite ist die Seite [saisie.xhtml]
12.2.2. Das Stylesheet
Die Datei [styles.css] sieht wie folgt aus:
<?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>
Hier sind einige Beispiele für JSF-Code, der diese Stile verwendet:
Simulationsansicht
.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"
Das Ergebnis sieht wie folgt aus:

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

12.2.3. Die Meldungsdatei
Die Meldungsdatei [messages_fr.properties] lautet wie folgt:
headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">
12.2.4. Die [Business]-Schicht
Die simulierte [Business]-Schicht wird wie folgt geändert:
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
- Zeile 8: die Liste der Mitarbeiter
- Zeile 7: dieselbe Liste als Wörterbuch, indiziert nach den Sozialversicherungsnummern der Mitarbeiter
- Zeilen 24–39: die Methode findAllEmployees, die die Liste der Mitarbeiter zurückgibt. Diese Methode erstellt eine fest codierte Liste und verweist über das Feld employees aus Zeile 8 darauf.
- Zeilen 27–33: Es werden eine Liste und ein Wörterbuch mit zwei Mitarbeitern erstellt
- Zeile 35: Ein Mitarbeiter wird zur Liste „employees“ (Zeile 8) hinzugefügt, jedoch nicht zum Wörterbuch „hashEmployees“ (Zeile 7). Dies geschieht, damit der Mitarbeiter im Dropdown-Menü „employee“ erscheint, aber von der Methode „calculatePayroll“ (Zeile 14) nicht erkannt wird, was dazu führt, dass eine Ausnahme ausgelöst wird (Zeile 17).
- Zeilen 11–21: Die Methode `calculatePayroll`
- Zeile 14: Der Mitarbeiter wird anhand seiner Sozialversicherungsnummer im Wörterbuch `hashEmployees` gesucht. Wird er nicht gefunden, wird eine Ausnahme ausgelöst (Zeilen 16–18). Somit erhalten wir eine Ausnahme für den Mitarbeiter mit der Sozialversicherungsnummer X, der in Zeile 35 zur Liste `employees`, nicht aber zum Wörterbuch `hashEmployees` hinzugefügt wurde.
- Zeile 20: Eine fiktive Gehaltsabrechnung wird erstellt und zurückgegeben.
12.3. Die Beans der Anwendung
Es gibt drei Beans mit drei verschiedenen Gültigkeitsbereichen:
![]() |
12.3.1. Die ApplicationData-Bean
Die ApplicationData-Bean hat den Anwendungsbereich:
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;
}
}
- Zeile 11: Die Annotation @Named macht die Klasse zu einem Managed Bean. Beachten Sie, dass wir im Gegensatz zum vorherigen Projekt die Annotation @ManagedBean nicht verwendet haben. Der Grund dafür ist, dass eine Referenz auf diese Klasse mithilfe der Annotation @Inject in eine andere Klasse injiziert werden muss und @Inject nur Klassen mit der Annotation @Named injiziert.
- Zeile 12: Die Annotation @ApplicationScoped macht die Klasse zu einem Objekt mit Anwendungsbereich. Beachten Sie, dass die Annotationsklasse [javax.enterprise.context.ApplicationScoped] (Zeile 6) lautet und nicht [javax.faces.bean.ApplicationScoped], wie bei den Beans des vorherigen Projekts.
Die ApplicationData-Bean erfüllt zwei Zwecke:
- Zeile 16: Aufrechterhaltung einer Referenz auf die [Business]-Schicht,
- Zeilen 18–19: Definition eines Loggers, der von anderen Beans verwendet werden kann, um Protokolle an die GlassFish-Konsole zu senden.
12.3.2. Die SessionData-Bean
Die SessionData-Bean hat den Gültigkeitsbereich „session“:
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
...
}
- Zeile 13: Die Klasse „SessionData“ ist ein Managed Bean (@Named), das in andere Managed Beans injiziert werden kann,
- Zeile 14: Sie hat den Gültigkeitsbereich „Session“ (@SessionScoped),
- Zeilen 18–19: Eine Referenz auf die ApplicationData-Bean wird in sie injiziert (@Inject),
- Zeilen 21–32: die Anwendungsdaten, die über mehrere Sitzungen hinweg beibehalten werden müssen.
- Zeile 21: die Liste der vom Benutzer durchgeführten Simulationen,
- Zeile 22: die Nummer der zuletzt gespeicherten Simulation,
- Zeile 23: die zuletzt durchgeführte Simulation,
- Zeilen 25–30: die Menüoptionen,
- Zeile 32: die Anwendungssprache.
Zeilen 39–44: Die init-Methode wird nach der Instanziierung der Klasse ausgeführt (@PostConstruct). Hier dient sie lediglich dazu, ihre Ausführung zu protokollieren. Da die Klasse im Session-Bereich gilt, müssen wir sicherstellen können, dass sie pro Benutzer nur einmal ausgeführt wird. Zeile 42: Die Methode verwendet den in der ApplicationData-Klasse definierten Logger. Aus diesem Grund mussten wir eine Referenz auf diese Bean injizieren (Zeilen 18–19).
12.3.3. Die Formular-Bean
Die Form-Bean ist anforderungsgebunden:
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
...
}
- Zeile 17: Die Klasse ist ein Managed Bean (@Named),
- Zeile 18, anforderungsgebunden (@RequestScoped),
- Zeilen 24–25: Injektion einer Referenz auf die anwendungsweit gültige Bean ApplicationData,
- Zeilen 26–27: Injektion einer Referenz auf die Bean „SessionData“ mit Sitzungsgültigkeit.
12.4. Die Anwendungsseiten
![]() |
12.4.1. [layout.xhtml]
Die Seite [layout.xhtml] ist für das Layout aller Ansichten zuständig:
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
...
}
Jede Ansicht besteht aus den folgenden Elementen:
- einer Kopfzeile, die vom Fragment [entete.xhtml] (Zeile 24) angezeigt wird,
- einem Hauptteil, der aus zwei Fragmenten namens part1 (Zeilen 26–28) und part2 (Zeile 29) besteht,
- Zeile 8: Das Attribut „locale“ gewährleistet die Internationalisierung der Seiten. Hier gibt es nur eines: fr_FR,
- Zeilen 14–19: JavaScript-Code.
Die Darstellung der Seite [layout.xhtml] sieht wie folgt aus:
![]() |
- in [1] die Kopfzeile [entete.xhtml],
- in [2] das Fragment „part1“.
12.4.2. Die Kopfzeile [entete.xhtml]
Der Code für die Seite [entete.xhtml] lautet wie folgt:
<?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>
- Zeile 12: der Anwendungstitel,
- Zeilen 16–21: die sechs Links, die den sechs Aktionen entsprechen, die der Benutzer ausführen kann. Diese Links werden (Render-Attribut) durch Boolesche Werte in der SessionData-Bean gesteuert,
- Zeile 17: Ein Klick auf den Link [Clear Simulation] löst die Ausführung der JavaScript-Funktion raz aus. Diese Funktion wurde in der Vorlage [layout.xhtml] definiert,
- Zeile 18: Das Attribut immediate=true stellt sicher, dass die Datenvalidität nicht überprüft wird, bevor die Methode [Form].enregistrerSimulation ausgeführt wird. Dies ist beabsichtigt. Möglicherweise möchten Sie die zuletzt vor einer Minute durchgeführte Simulation speichern, auch wenn die seitdem in das Formular eingegebenen (aber noch nicht validierten) Daten ungültig sind. Dasselbe gilt für die Aktionen [Simulationen anzeigen], [Simulation löschen], [Zurück zum Simulator] und [Sitzung beenden].
12.5. Anwendungsfälle für die Anwendung
12.5.1. Anzeigen der Startseite
Die Startseite ist die folgende Seite [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>
- Zeile 8: Es wird innerhalb der Seite [layout.xhtml] angezeigt,
- Zeile 9: anstelle des Fragments mit dem Namen part1. In diesem Fragment wird die Seite [saisie2.xhtml] angezeigt:
<?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>
Dieser Code zeigt die folgende Ansicht an:
![]() |
Frage: Vervollständigen Sie Zeile 13 des XHTML-Codes. Die Liste der Elemente im Kombinationsfeld „employee“ wird von einer Methode der [Form]-Bean bereitgestellt. Schreiben Sie diese Methode. Bei den im Kombinationsfeld angezeigten Elementen wird die Eigenschaft „itemValue“ auf die Sozialversicherungsnummer des Mitarbeiters gesetzt, und die Eigenschaft „itemLabel“ ist eine Zeichenfolge, die aus dem Vor- und Nachnamen des Mitarbeiters besteht.
Frage: Wie sollte die [SessionData]-Bean initialisiert werden, damit bei der ersten GET-Anfrage an das Formular das oben gezeigte Kopfmenü angezeigt wird?
12.5.2. Die Aktion [ faireSimulation]
Der JSF-Code für die Aktion [runSimulation] lautet wie folgt:
<?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>
Die Aktion [faireSimulation] berechnet eine Gehaltsabrechnung:

Aus der vorherigen Seite erhalten wir das folgende Ergebnis:
![]() |
![]() |
Die Simulation wird auf der folgenden Seite [simulation.xhtml] angezeigt:
<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>
- Zeile 8: Die Seite [simulation.xhtml] wird in die Seite [layout.xhtml] eingefügt,
- Zeilen 9–11: Der Eingabebereich wird im Fragment „part1“ der Layout-Seite angezeigt,
- Zeilen 12–91: Die Simulation wird im Fragment „part2“ der Layout-Seite angezeigt.
Frage: Schreiben Sie die Methode [doSimulation] für die Klasse [Form]. Die Simulation wird in der SessionData-Bean gespeichert.
12.5.3. Fehlerbehandlung
Wir möchten in der Lage sein, Ausnahmen, die während der Berechnung einer Simulation auftreten können, ordnungsgemäß zu behandeln. Zu diesem Zweck verwendet der Code für die Methode [runSimulation] einen try/catch-Block:
<?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>
Die Liste der in Zeile 15 erzeugten Fehler lautet wie folgt:
// 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";
}
}
Die Klasse „Error“ ist wie folgt definiert:
// le modèle des vues
...
private List<Erreur> erreurs=new ArrayList<Erreur>();
...
Fehler sind Ausnahmen, deren Klassenname im Feld „class“ und deren Meldung im Feld „message“ gespeichert wird.
Die in der Methode [faireSimulation] erstellte Fehlerliste besteht aus:
- der ursprünglichen Ausnahme th vom Typ Throwable, die aufgetreten ist,
- ihrer Ursache th.getCause(), falls vorhanden,
- der Ursache der Ursache h.getCause().getCause(), falls vorhanden.
- ...
Hier ist ein Beispiel für eine Fehlerliste:

Oben existiert der Mitarbeiter [Z Y] nicht im Mitarbeiter-Wörterbuch, das von der simulierten [Business]-Schicht verwendet wird. In diesem Fall löst die simulierte [Business]-Schicht eine Ausnahme aus (Zeile 6 unten):
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
...
}
Das Ergebnis lautet wie folgt:

Diese Ansicht wird auf der folgenden Seite [errors.xhtml] angezeigt:
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);
}
...
}
Frage: Vervollständigen Sie die Methode [faireSimulation] so, dass im Falle einer Ausnahme die Ansicht [vueErreur] angezeigt wird.
12.5.4. Die Aktion [ effacerSimulation]
Die Aktion [clearSimulation] ermöglicht es dem Benutzer, zu einem leeren Formular zurückzukehren:
![]() |
Der JSF-Code für den Link [clearSimulation] lautet wie folgt:
<?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>
Ein Klick auf den Link [ClearSimulation] löst zunächst einen Aufruf der JavaScript-Funktion raz() aus. Diese Methode ist in der Seite [layout.xhtml] definiert:
<h:commandLink id="cmdEffacerSimulation" onclick="raz()" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>
Die Zeilen 4–6 ändern die übermittelten Werte. Beachten Sie, dass
- die übermittelten Werte gültig sind, d. h., sie bestehen die Validierungsprüfungen für die Eingabefelder „hoursWorked“ und „daysWorked“.
- Die Funktion `raz` sendet das Formular nicht ab. Stattdessen wird das Formular über den Link `cmdEffacerSimulation` übermittelt. Diese Übermittlung erfolgt, nachdem die JavaScript-Funktion `raz` ausgeführt wurde.
Während der Übermittlung durchlaufen die übermittelten Werte den normalen Prozess: Validierung, dann Zuweisung zu den Modellfeldern. Diese sind in der Klasse [Form] wie folgt definiert:
<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;
Diese drei Felder erhalten die drei übermittelten Werte {"0", "0", "0"}. Sobald diese Zuweisung abgeschlossen ist, wird die Methode clearSimulation ausgeführt.
Frage: Schreiben Sie die Methode [effacerSimulation] für die Klasse [Form]. Stellen Sie sicher, dass:
-
nur das Eingabefeld angezeigt wird,
-
das Kombinationsfeld auf den ersten Eintrag gesetzt ist,
-
die Eingabefelder „hoursWorked“ und „daysWorked“ leere Zeichenfolgen anzeigen.
12.5.5. Die Aktion [ enregistrerSimulation]
Der JSF-Code für den Link [saveSimulation] lautet wie folgt:
...
Die mit dem Link verknüpfte Aktion [saveSimulation] speichert die aktuelle Simulation in einer Liste von Simulationen, die in der Klasse [SessionData] verwaltet wird:
<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}" rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>
Die Klasse „Simulation“ im Web-Entity- , sieht wie folgt aus:
private List<Simulation> simulations=new ArrayList<Simulation>();
Mit dieser Klasse können Sie eine vom Benutzer erstellte Simulation speichern:
- Zeile 11: die Simulationsnummer,
- Zeile 12: die berechnete Gehaltsabrechnung,
- Zeile 13: die Anzahl der geleisteten Arbeitsstunden,
- Zeile 14: die Anzahl der gearbeiteten Tage.
Hier ist ein Beispiel für einen Datensatz:

Aus der vorherigen Seite erhalten wir folgendes Ergebnis:

Die Simulationsnummer wird mit jedem neuen Datensatz erhöht. Sie gehört zur SessionData-Bean:
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
...
}
- Zeile 2: die Nummer der zuletzt durchgeführten Simulation.
Die Methode [saveSimulation] kann wie folgt ablaufen:
- die Nummer der letzten Simulation aus dem [SessionData]-Bean abrufen und erhöhen,
- die neue Simulation zur Liste der Simulationen hinzufügen, die von der [SessionData]-Klasse verwaltet wird,
- die Simulationstabelle anzeigen:
![]() |
Die Simulationstabelle wird auf der Seite [simulations.xhtml] angezeigt:
// simulations
private List<Simulation> simulations = new ArrayList<Simulation>();
private int numDerniereSimulation = 0;
private Simulation simulation;
- Zeile 8: Die Seite [simulations.xhtml] wird in die Seite [layout.xhtml] eingefügt,
- Zeile 9: Anstelle des Fragments mit dem Namen part1
- Zeile 11: Das <h:dataTable>-Tag verwendet das Feld #{sessionData.simulations} als Datenquelle, d. h. das folgende Feld:
<?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>
-
Das Attribut var="simulation" legt den Namen der Variablen fest, die die aktuelle Simulation innerhalb des <h:datatable>-Tags repräsentiert
-
Das Attribut headerClass="simulationsHeaders" legt den Stil der Tabellenkopfzeilen fest.
-
Das Attribut
**columnClasses="...."** legt den Stil jeder Spalte in der Tabelle fest
Sehen wir uns eine der Tabellenspalten genauer an und schauen wir uns an, wie sie aufgebaut ist:
![]() |
Der JSF-Code für die Spalte „Name“ lautet wie folgt:
// simulations
private List<Simulation> simulations;
- Zeilen 2–4: Das Tag <f:facet name="header"> definiert die Spaltenüberschrift
- Zeile 5: Der Name des Mitarbeiters wird geschrieben:
- simulation bezieht sich auf das var-Attribut des <h:dataTable ...>-Tags:
<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 bezieht sich auf die aktuelle Simulation in der Liste der Simulationen: zuerst die 1., dann die 2., …
- (Fortsetzung)
- simulation.payroll bezieht sich auf das Feld „Payroll“ der aktuellen Simulation
- simulation.payroll.employee bezieht sich auf das Feld „Mitarbeiter“ innerhalb des Feldes „Lohnabrechnung“
- simulation.payroll.employee.name bezieht sich auf das Feld „name“ des Feldes „employee“
Die gleiche Vorgehensweise wird für alle Spalten der Tabelle wiederholt. Es gibt ein Problem mit der Spalte „Allowances“, die mit dem folgenden Code generiert wird:
<h:dataTable value="#{sessionData.simulations}" var="simulation" ...>
In Zeile 5 wird der Wert von simulation.indemnites angezeigt. Die Klasse Simulation verfügt jedoch nicht über ein Feld indemnites. Es ist wichtig, sich hier vor Augen zu halten, dass das Feld indemnites nicht direkt, sondern über die Methode simulation.getIndemnites() verwendet wird. Daher reicht es aus, dass diese Methode existiert. Das Feld „indemnites“ selbst muss nicht vorhanden sein. Die Methode „getIndemnites“ muss die Summe der Zulagen des Mitarbeiters zurückgeben. Dies erfordert eine Zwischenberechnung, da diese Summe nicht direkt in der Gehaltsabrechnung verfügbar ist. Die Methode „getIndemnites“ wird in Abschnitt 12.5.5 bereitgestellt.
Frage: Schreiben Sie die Methode [enregistrerSimulation] für die Klasse [Form].
12.5.6. Die Aktion [ retourSimulateur]
Der JSF-Code für den Link [retourSimulateur] lautet wie folgt:
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.indemnites']}"/>
</f:facet>
<h:outputText value="#{simulation.indemnites}"/>
</h:column>
Die mit dem Link verknüpfte Aktion [returnSimulator] ermöglicht es dem Benutzer, von der Ansicht [simulationsView] zur Ansicht [inputsView] zurückzukehren:

Das erzielte Ergebnis:

Frage: Schreiben Sie die Methode [retourSimulateur] für die Klasse [Form]. Das angezeigte Eingabeformular muss leer sein, wie oben.
12.5.7. Die Aktion [viewSimulations]
Der JSF-Code für den Link [viewSimulations] lautet wie folgt:
<h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>
Die mit dem Link verknüpfte Aktion [viewSimulations] ermöglicht es dem Benutzer, die Tabelle der Simulationen anzuzeigen, unabhängig vom Status seiner Eingaben:

Das erzielte Ergebnis:

Frage: Schreiben Sie die Methode [viewSimulations] für die Klasse [Form].
Wir stellen sicher, dass, wenn die Liste der Simulationen leer ist, die Ansicht [vueSimulationsVides] angezeigt wird:

Um die obige Ansicht anzuzeigen, verwenden wir die folgende Seite [simulationsVides.xhtml]:
<h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>
12.5.8. Die Aktion [ removeSimulation]
Der Benutzer kann Simulationen aus seiner Liste entfernen:

Das Ergebnis sieht wie folgt aus:

Wenn wir die letzte Simulation oben entfernen, erhalten wir das folgende Ergebnis:

Der JSF-Code für die Spalte [Entfernen] in der Simulationstabelle lautet wie folgt:
<?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>
- Zeile 5: Der Link [Entfernen] ist mit der Methode [removeSimulation] der Klasse [Form] verknüpft. Diese Methode benötigt die Nummer der zu entfernenden Simulation. Diese wird durch das Tag <f:setPropertyActionListener> in Zeile 8 bereitgestellt. Dieses Tag hat zwei Attribute, target und value: Das Attribut target gibt ein Feld im Modell an, dem der Wert des Attributs value zugewiesen wird. Hier wird die Nummer der zu löschenden Simulation, #{simulation.num}, dem Feld numSimulationToDelete der Klasse [Form] zugewiesen:
<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>
Wenn die Methode [removeSimulation] der Klasse [Form] ausgeführt wird, kann sie den zuvor im Feld numSimulationToDelete gespeicherten Wert verwenden.
Frage: Schreiben Sie die Methode [retirerSimulation] der Klasse [Form].
12.5.9. Die Aktion [ terminateSession]
Der JSF-Code für den Link [End Session] lautet wie folgt:
// the view model
...
private Integer numSimulationToDelete;
Die mit dem Link verknüpfte Aktion [EndSession] ermöglicht es dem Benutzer, seine Sitzung zu beenden und zum leeren Eingabeformular zurückzukehren:


Falls der Benutzer eine Liste von Simulationen hatte, wird diese gelöscht. Außerdem wird die Simulationsnummerierung auf 1 zurückgesetzt.
Frage: Schreiben Sie die Methode [endSession] für die Klasse [Form].
12.6. Integration der Webschicht in eine dreischichtige Architektur ( , JSF/EJB)
Die Architektur der bisherigen Webanwendung sah wie folgt aus:
![]() |
Wir ersetzen die simulierte [Business]-Schicht durch die in Abschnitt 7.1 durch EJBs implementierten [Business-, DAO- und JPA]-Schichten:
![]() |
Praxisübung: Integrieren Sie die JSF- und EJB-Schichten gemäß der Methodik in Abschnitt 11.
![]() |














