12. Wersja 7 – Aplikacja internetowa PAM z wieloma widokami / wieloma stronami
Wracamy tutaj do pierwotnej architektury, w której warstwa [métier] była symulowana. Wiemy już, że można ją z łatwością zastąpić rzeczywistą warstwą [métier]. Symulowana warstwa [métier] ułatwia przeprowadzanie testów.
![]() |
Aplikacja JSF jest typu MVC (Model-Widok-Kontroler):
- serwlet [Faces Servlet] jest kontrolerem generycznym dostarczanym przez JSF. Kontroler ten jest rozszerzany przez specyficzne dla aplikacji procedury obsługi zdarzeń. Dotychczasowe procedury obsługi zdarzeń były metodami klas pełniących rolę szablonów dla stron JSF
- Strony JSF wysyłają odpowiedzi do przeglądarki klienta. Są to widoki aplikacji.
- Strony JSF zawierają elementy dynamiczne, które nazywane są modelem strony. Należy przypomnieć, że według niektórych autorów model obejmuje elementy przetwarzane przez aplikację, takie jak na przykład klasy FeuilleSalaire lub Employe. Aby odróżnić te dwa modele, można mówić o modelu aplikacji i modelu strony JSF.
W architekturze JSF przejście ze strony JSFi na stronę JSFj może stanowić problem.
- Wyświetlono stronę JSFi. Z tej strony użytkownik wywołuje stronę POST poprzez dowolne zdarzenie [1]
- w JSF, to POST zostanie przetworzone [2a,2b] zazwyczaj przez metodę C modelu Mi strony JSFi. Można powiedzieć, że metoda C jest kontrolerem drugorzędnym.
- Jeśli po zakończeniu działania tej metody strona JSFj ma zostać wyświetlona, kontroler C musi:
- zaktualizować [2c] – model Mj strony JSFj
- przekazać kontrolerowi głównemu [2a] klucz nawigacyjny, który umożliwi wyświetlenie strony JSFj
Krok 1 wymaga, aby szablon Mi strony JSFi zawierał odwołanie do szablonu Mj strony JSFj. To nieco komplikuje sprawę, powodując wzajemną zależność szablonów Mi. W rzeczywistości menedżer C szablonu Mi, który aktualizuje szablon Mj, musi znać ten ostatni. Jeśli zajdzie potrzeba zmiany szablonu Mj, konieczna będzie również zmiana menedżera C szablonu Mi.
Istnieje jednak przypadek, w którym można uniknąć wzajemnej zależności modeli: jest to sytuacja, w której istnieje jeden model M, który obsługuje wszystkie strony JSF. Architektura ta ma zastosowanie wyłącznie w aplikacjach posiadających zaledwie kilka widoków, ale w takim przypadku okazuje się bardzo prosta w użyciu. Właśnie z niej obecnie korzystamy.
W tym kontekście architektura aplikacji wygląda następująco:
![]() |
12.1. Widoki aplikacji
Użytkownikowi będą prezentowane następujące widoki:
-
widok [VueSaisies], który przedstawia formularz symulacji
-
widok [VueSimulation] służący do wyświetlania szczegółowych wyników symulacji:


- widok [VueSimulations], zawierający listę symulacji przeprowadzonych przez klienta

- widok [VueSimulationsVides], który wskazuje, że klient nie ma żadnych symulacji lub nie ma ich już:

- widok [VueErreur], który wskazuje na jeden lub więcej błędów:

12.2. Projekt NetBeans dla warstwy [web]
Projekt NetBeans tej wersji będzie następującym projektem Maven:
![]() |
- w [1], pliki konfiguracyjne,
- w [2] – strony, w JSF,
- w [3] – arkusz stylów i obraz tła widoków,
- w pliku [4] – klasy warstwy [web],
- w pliku [5] – niższe warstwy aplikacji,
- w pliku [6] – plik komunikatów do internacjonalizacji aplikacji,
- w pliku [7] – zależności projektu.
Przyjrzyjmy się niektórym z tych elementów.
12.2.1. Pliki konfiguracyjne
Pliki [web.xml] i [faces-config.xml] są identyczne jak w poprzednim projekcie, z wyjątkiem jednego szczegółu w pliku [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>
- wiersze 4–6: stroną główną jest strona [saisie.xhtml]
12.2.2. Arkusz stylów
Plik [styles.css] ma następującą treść:
.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
}
Oto przykłady kodu JSF wykorzystujące te style:
Widok symulacji
<h:dataTable value="#{form.simulations}" var="simulation"
headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
Otrzymany wynik wygląda następująco:

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

12.2.3. Plik komunikatów
Plik komunikatów [messages_fr.properties] ma następującą treść:
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. Warstwa [métier]
Symulowana warstwa [métier] została zmodyfikowana w następujący sposób:
package metier;
...
public class Metier implements ImetierLocal, Serializable {
// lista pracowników
private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
private List<Employe> listEmployes;
// pobierz listę płac
public FeuilleSalaire calculerFeuilleSalaire(String SS,
double nbHeuresTravaillées, int nbJoursTravaillés) {
// pobieranie danych pracownika
Employe e=hashEmployes.get(SS);
// zwracamy wyjątek, jeśli pracownik nie istnieje
if(e==null){
throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
}
// zwracamy fikcyjny list płac
return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),e.getIndemnite(),new ElementsSalaire(100,100,100,100,100));
}
// lista pracowników
public List<Employe> findAllEmployes() {
if(listEmployes==null){
// tworzy się listę trzech pracowników
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)));
// słownik pracowników
for(Employe e:listEmployes){
hashEmployes.put(e.getSS(),e);
}
// dodajemy pracownika, który nie będzie figurował w słowniku
listEmployes.add(new Employe("X","Y","Z","La brûlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12)));
}
// wyświetlanie listy pracowników
return listEmployes;
}
}
- wiersz 8: lista pracowników
- wiersz 7: ta sama lista w postaci słownika indeksowanego według numeru SS pracowników
- wiersze 24–39: metoda findAllEmployes, która zwraca listę pracowników. Metoda ta tworzy listę statyczną i odwołuje się do pola employés z wiersza 8.
- wiersze 27–33: tworzona jest lista oraz słownik zawierający dwóch pracowników
- wiersz 35: pracownik zostaje dodany do listy pracowników (wiersz 8), ale nie do słownika hashEmployes (wiersz 7). Ma to na celu wyświetlenie go w liście rozwijanej pracowników, ale jednocześnie uniemożliwienie jego rozpoznania przez metodę calculerFeuilleSalaire (wiersz 14), co spowoduje wygenerowanie wyjątku (wiersz 17).
- wiersze 11–21: metoda calculerFeuilleSalaire
- wiersz 14: pracownik jest wyszukiwany w słowniku hashEmployes na podstawie jego numeru SS. Jeśli nie zostanie znaleziony, generowany jest wyjątek (wiersze 16–18). W ten sposób otrzymamy wyjątek dla pracownika o numerze SS X, dodanego w wierszu 35 do listy pracowników, ale nieznajdującego się w słowniku hashEmployes.
- W wierszu 20 tworzona jest i zwracana fikcyjna karta płacowa.
12.3. Beany aplikacji
Będą trzy komponenty typu bean o trzech różnych zakresach:
![]() |
12.3.1. Bean ApplicationData
Bean ApplicationData będzie miał zakres 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 {
// warstwa biznesowa
private IMetierLocal metier = new Metier();
// moduł rejestrujący
private boolean logsEnabled = true;
private final Logger logger = Logger.getLogger("pam");
public ApplicationData() {
}
@PostConstruct
public void init() {
// log
if (isLogsEnabled()) {
logger.info("ApplicationData");
}
}
// metody pobierające i ustawiające
...
}
- wiersz 11: adnotacja @Named sprawia, że klasa staje się beanem zarządzanym. Należy zauważyć, że w odróżnieniu od poprzedniego projektu nie zastosowano adnotacji @ManagedBean. Wynika to z faktu, że odwołanie do tej klasy musi zostać wstrzyknięte do innej klasy za pomocą adnotacji @Inject, a ta wstrzykuje wyłącznie klasy opatrzone adnotacją @Named.
- wiersz 12: adnotacja @ApplicationScoped sprawia, że klasa staje się obiektem o zasięgu aplikacji. Należy zauważyć, że klasą adnotacji jest [javax.enterprise.context.ApplicationScoped] (wiersz 6), a nie [javax.faces.bean.ApplicationScoped], jak w przypadku beanów z poprzedniego projektu.
Bean ApplicationData służy do dwóch rzeczy:
- wiersz 16: utrzymywanie odwołania do warstwy [métier],
- wiersze 18–19: zdefiniowanie modułu logującego, z którego będą mogły korzystać inne komponenty w celu rejestrowania logów na konsoli Glassfish.
12.3.2. Bean SessionData
Bean SessionData będzie miał zasięg sesji:
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 {
// aplikacja
@Inject
private ApplicationData applicationData;
// symulacje
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;
// ustawienia regionalne
private String locale="fr_FR";
// konstruktor
public SessionData() {
}
@PostConstruct
public void init() {
// dziennik
if (applicationData.isLogsEnabled()) {
applicationData.getLogger().info("SessionData");
}
}
// zarządzanie 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);
}
// metody pobierające i ustawiające
...
}
- wiersz 13: klasa SessionData jest beanem zarządzanym (@Named), który może być wstrzykiwany do innych beanów zarządzanych,
- wiersz 14: ma zasięg sesji (@SessionScoped),
- wiersze 18–19: do niego wstrzykiwane jest odwołanie do fasoli ApplicationData (@Inject),
- wiersze 21–32: dane aplikacji, które muszą być zachowane między sesjami.
- wiersz 21: lista symulacji wykonanych przez użytkownika,
- wiersz 22: numer ostatniej zapisanej symulacji,
- wiersz 23: ostatnia przeprowadzona symulacja,
- wiersze 25–30: opcje menu,
- wiersz 32: ustawienia regionalne aplikacji.
Wiersze 39–44: metoda init jest wykonywana po utworzeniu instancji klasy (@PostConstruct). W tym przypadku służy ona wyłącznie do pozostawienia śladu jej wykonania. Należy sprawdzić, czy jest ona wykonywana tylko raz na użytkownika, ponieważ klasa ma zasięg sesji. Wiersz 42: metoda korzysta z logera zdefiniowanego w klasie ApplicationData. Z tego powodu konieczne było wstrzyknięcie odwołania do tego beana (wiersze 18–19).
12.3.3. Bean Form
Bean Form ma zakres 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() {
}
// inne komponenty
@Inject
private ApplicationData applicationData;
@Inject
private SessionData sessionData;
// model widoków
private String comboEmployesValue = "";
private String heuresTravaillées = "";
private String joursTravaillés = "";
private Integer numSimulationToDelete;
private List<Erreur> erreurs = new ArrayList<Erreur>();
private FeuilleSalaire feuilleSalaire;
// lista pracowników
public List<Employe> getEmployes(){
return applicationData.getMetier().findAllEmployes();
}
// akcja 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() {
...
}
// metody getter i setter
...
}
- w wierszu 17 klasa jest beanem zarządzanym (@Named),
- w wierszu 18 – o zasięgu żądania (@RequestScoped),
- wiersze 24–25: wstrzyknięcie odwołania do beana o zasięgu aplikacji ApplicationData,
- wiersze 26–27: wstrzyknięcie odwołania do bean o zasięgu sesji SessionData.
12.4. Strony aplikacji
![]() |
12.4.1. [layout.xhtml]
Strona [layout.xhtml] odpowiada za układ wszystkich widoków:
<?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(){
// zmiana wartości przesłanych
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">
<!-- nagłówek -->
<ui:include src="entete.xhtml" />
<!-- treść -->
<ui:insert name="part1" >
Gestion des assistantes maternelles
</ui:insert>
<ui:insert name="part2"/>
</h:form>
</h:body>
</f:view>
</html>
Każdy widok składa się z następujących elementów:
- nagłówek wyświetlany przez fragment [entete.xhtml] (wiersz 24),
- treść złożona z dwóch fragmentów o nazwach part1 (wiersze 26–28) i part2 (wiersz 29),
- wiersz 8: atrybut „locale” zapewnia internacjonalizację stron. W tym przypadku będzie tylko jeden: fr_FR,
- wiersze 14–19: kod JavaScript.
Wygląd strony [layout.xhtml] jest następujący:
![]() |
- w [1] nagłówek [entete.xhtml],
- w [2] – fragment „part1”.
12.4.2. Nagłówek [entete.xhtml]
Kod strony [entete.xhtml] jest następujący:
<?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>
<!-- nagłówek -->
<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>
- wiersz 12: tytuł aplikacji,
- wiersze 16–21: sześć linków odpowiadających sześciu czynnościom, które może wykonać użytkownik. Linki te są kontrolowane (atrybut rendered) przez wartości logiczne z bean’a SessionData,
- wiersz 17: kliknięcie linku [Effacer la simulation] powoduje wykonanie funkcji JavaScript o nazwie „raz”. Została ona zdefiniowana w modelu [layout.xhtml],
- wiersz 18: atrybut immediate=true powoduje, że poprawność danych nie jest sprawdzana przed wykonaniem metody [Form].enregistrerSimulation. Jest to zamierzone. Można chcieć zapisać ostatnią symulację przeprowadzoną minutę temu, nawet jeśli dane wprowadzone od tego czasu w formularzu (ale jeszcze nie zweryfikowane) są nieprawidłowe. To samo dotyczy akcji [Voir les simulations], [Effacer la simulation], [Retour au simulateur] i [Terminer la session].
12.5. Przykłady zastosowań aplikacji
12.5.1. Wyświetlanie strony głównej
Strona główna to następująca strona o numerze [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>
- wiersz 8: wyświetla się wewnątrz strony [layout.xhtml],
- wiersz 9: w miejscu fragmentu o nazwie part1. W tym fragmencie wyświetlana jest strona [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>
Ten kod wyświetla następujący widok:
![]() |
Zadanie: uzupełnij wiersz 13 kodu XHTML. Lista elementów listy rozwijanej pracowników jest dostarczana przez metodę fasoli [Form]. Napisz tę metodę. Elementy wyświetlane w liście rozwijanej będą miały właściwość itemValue równą numerowi SS pracownika, a właściwość itemLabel będzie ciągiem znaków składającym się z imienia i nazwiska tego pracownika.
Pytanie: w jaki sposób należy zainicjować bean [SessionData], aby po wysłaniu pierwszego żądania GET do formularza menu w nagłówku wyglądało tak, jak pokazano powyżej?
12.5.2. Akcja [faireSimulation]
Kod JSF akcji [faireSimulation] wygląda następująco:
<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>
Akcja [faireSimulation] oblicza listę płac:

Na podstawie poprzedniej strony otrzymujemy następujący wynik:
![]() |
![]() |
Symulacja jest wyświetlana na następującej stronie [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>
- w wierszu 8 strona [simulation.xhtml] jest wstawiana do strony [layout.xhtml],
- wiersze 9–11: obszar wprowadzania danych jest wyświetlany we fragmencie part1 strony layout,
- wiersze 12–91: symulacja jest wyświetlana w fragmencie part2 strony layout.
Zadanie: napisać metodę [faireSimulation] klasy [Form]. Symulacja zostanie zapisana w bean SessionData.
12.5.3. Obsługa błędów
Chcemy zapewnić prawidłowe obsługiwanie wyjątków, które mogą wystąpić podczas obliczania symulacji. W tym celu kod metody [faireSimulation] będzie wykorzystywał blok try / catch:
// działanie z menu
public String faireSimulation(){
try{
// obliczanie listy płac
feuilleSalaire= ...
// wyświetla się symulacja
...
// aktualizacja menu
...
// przechodzi do widoku symulacji
return "simulation";
}catch(Throwable th){
// czyści się listę poprzednich błędów
...
// tworzy się nową listę błędów
...
// wyświetla się widok vueErreur
...
// aktualizowanie menu
...
// wyświetla się widok błędu
return "erreurs";
}
}
Lista błędów utworzona w wierszu 15 wygląda następująco:
// szablon widoków
...
private List<Erreur> erreurs=new ArrayList<Erreur>();
...
Klasa „Błąd” jest zdefiniowana w następujący sposób:
package web.entities;
public class Erreur {
public Erreur() {
}
// pole
private String classe;
private String message;
// konstruktor
public Erreur(String classe, String message){
this.setClasse(classe);
this.message=message;
}
// metody pobierające i ustawiające
...
}
Błędy będą stanowić wyjątki, których nazwa klasy jest zapisywana w polu „klasa”, a komunikat w polu „komunikat”.
Lista błędów utworzona w metodzie [faireSimulation] składa się z:
- pierwotnego wyjątku th typu Throwable, który wystąpił,
- jej przyczyny th.getCause(), o ile taka istnieje,
- przyczyny tej przyczyny o typie h.getCause().getCause(), o ile istnieje.
- ...
Oto przykładowa lista błędów:

W powyższym przykładzie pracownik [Z Y] nie istnieje w słowniku pracowników używanym przez symulowaną warstwę [métier]. W takim przypadku symulowana warstwa [métier] zgłasza wyjątek (wiersz 6 poniżej):
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
// pobieramy pracownika
Employe e=hashEmployes.get(SS);
// zwracamy wyjątek, jeśli pracownik nie istnieje
if(e==null){
throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
}
...
}
Otrzymano następujący wynik:

Ten widok jest wyświetlany przez następującą stronę [erreurs.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">
<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>
Zadanie: uzupełnić metodę [faireSimulation] tak, aby w przypadku wystąpienia wyjątku wyświetlała widok [vueErreur].
12.5.4. Akcja [effacerSimulation]
Akcja [effacerSimulation] umożliwia użytkownikowi wyświetlenie pustego formularza:
![]() |
Kod JSF linku [effacerSimulation] jest następujący:
<h:commandLink id="cmdEffacerSimulation" onclick="raz()" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>
Kliknięcie linku [EffacerSimulation] powoduje najpierw wywołanie funkcji JavaScript raz(). Metoda ta jest zdefiniowana na stronie [layout.xhtml]:
<script type="text/javascript">
function raz(){
// zmiana przesłanych wartości
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>
Wiersze 4–6 zmieniają wartości przesłane. Należy zauważyć, że
- przesłane wartości są prawidłowe (c.a.d) i przejdą testy walidacji pól wprowadzania danych (heuresTravaillees oraz joursTravailles).
- Funkcja raz nie wysyła formularza. Zostanie on bowiem wysłany za pomocą linku cmdEffacerSimulation. Operacja post zostanie wykonana po uruchomieniu funkcji JavaScript raz.
W trakcie działania funkcji post przesłane wartości przejdą standardową ścieżkę: walidację, a następnie przypisanie do pól modelu. W klasie [Form] są to następujące pola:
// szablon widoków
private String comboEmployesValue;
private String heuresTravaillées;
private String joursTravaillés;
...
W tych trzech polach zostaną zapisane trzy wartości {"0", „0”, „0”}. Po dokonaniu tego przypisania zostanie wykonana metoda effacerSimulation.
Pytanie: Należy napisać metodę [effacerSimulation] klasy [Form]. Należy zadbać o to, aby:
-
wyświetlany był tylko obszar wprowadzania danych,
-
lista rozwijana była ustawiona na pierwszym elemencie,
-
pola wprowadzania danych heuresTravaillees i joursTravailles wyświetlały puste ciągi znaków.
12.5.5. Akcja [enregistrerSimulation]
Kod JSF powiązany z linkiem [enregistrerSimulation] wygląda następująco:
<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}" rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>
Akcja [enregistrerSimulation] powiązana z tym linkiem pozwala zapisać bieżącą symulację na liście symulacji przechowywanej w klasie [SessionData]:
private List<Simulation> simulations=new ArrayList<Simulation>();
Klasa „ : Simulation” ma następujący wygląd:
package web.entities;
import metier.FeuilleSalaire;
public class Simulation {
public Simulation() {
}
// pola symulacji
private Integer num;
private FeuilleSalaire feuilleSalaire;
private String heuresTravaillées;
private String joursTravaillés;
// konstruktor
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();
}
// metody pobierające i ustawiające
...
}
Klasa ta umożliwia zapisanie symulacji wykonanej przez użytkownika:
- wiersz 11: numer symulacji,
- wiersz 12: rozliczenie wynagrodzenia, które zostało obliczone,
- wiersz 13: liczba przepracowanych godzin,
- wiersz 14: liczba przepracowanych dni.
Oto przykładowy wpis:

Na podstawie poprzedniej strony otrzymujemy następujący wynik:

Numer symulacji to liczba zwiększana przy każdym nowym wpisie. Należy on do bean’a SessionData:
// symulacje
private List<Simulation> simulations = new ArrayList<Simulation>();
private int numDerniereSimulation = 0;
private Simulation simulation;
- wiersz 2: numer ostatniej przeprowadzonej symulacji.
Metoda [enregistrerSimulation] może działać w następujący sposób:
- pobrać numer ostatniej symulacji z bean [SessionData] i zwiększyć go o 1,
- dodać nową symulację do listy symulacji zarządzanej przez klasę [SessionData],
- wyświetlić tabelę symulacji:
![]() |
Tabela symulacji jest wyświetlana przez stronę [simulations.xhtml]:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="layout.xhtml">
<ui:define name="part1">
<!-- tabela symulacji -->
<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>
- w wierszu 8 strona [simulations.xhtml] zostaje wstawiona do strony [layout.xhtml],
- w wierszu 9, w miejsce fragmentu o nazwie part1,
- w wierszu 11 tag <h:dataTable> wykorzystuje pole #{sessionData.simulations} jako źródło danych, c.a.d. następujące pole:
// symulacje
private List<Simulation> simulations;
-
atrybut var="simulation" określa nazwę zmiennej reprezentującej bieżącą symulację wewnątrz tagu <h:datatable>
-
atrybut headerClass="simulationsHeaders" określa styl nagłówków kolumn tabeli.
-
atrybut columnClasses="...." określa styl każdej z kolumn tabeli
Przyjrzyjmy się jednej z kolumn tabeli i zobaczmy, jak jest zbudowana:
![]() |
Kod JSF kolumny Nom wygląda następująco:
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.nom']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
</h:column>
- wiersze 2–4: tag <f:facet name="header"> określa tytuł kolumny
- wiersz 5: wpisano nazwisko pracownika:
- symulacja odnosi się do atrybutu var tagu <h:dataTable ...>:
<h:dataTable value="#{sessionData.simulations}" var="simulation" ...>
„symulacja” oznacza bieżącą symulację z listy symulacji: najpierw pierwszą, potem drugą, …
- (ciąg dalszy)
- simulation.feuilleSalaire odnosi się do pola feuilleSalaire bieżącej symulacji
- simulation.feuilleSalaire.employe odnosi się do pola „pracownik” w polu feuilleSalaire
- simulation.feuilleSalaire.employe.nom odnosi się do pola „nom” w polu „employe”
Ta sama procedura jest powtarzana dla wszystkich kolumn tabeli. Problem pojawia się w przypadku kolumny „Indemnités”, która jest generowana za pomocą następującego kodu:
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.indemnites']}"/>
</f:facet>
<h:outputText value="#{simulation.indemnites}"/>
</h:column>
W wierszu 5 wyświetlana jest wartość simulation.indemnites. Jednak klasa Simulation nie posiada pola „indemnites”. Należy pamiętać, że pole „indemnites” nie jest wykorzystywane bezpośrednio, lecz za pośrednictwem metody simulation.getIndemnites(). Wystarczy zatem, że ta metoda istnieje. Pole indemnites może natomiast nie istnieć. Metoda getIndemnites musi zwracać sumę dodatków pracownika. Wymaga to obliczeń pośrednich, ponieważ suma ta nie jest bezpośrednio dostępna w liście płac. Metoda getIndemnites została podana w paragrafie 12.5.5.
Zadanie: napisać metodę [enregistrerSimulation] klasy [Form].
12.5.6. Akcja [retourSimulateur]
Kod JSF powiązania [retourSimulateur] ma następującą postać:
<h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>
Akcja [retourSimulateur] powiązana z linkiem pozwala użytkownikowi powrócić z widoku [vueSimulations] do widoku [vueSaisies]:

Uzyskany wynik:

Zadanie: należy napisać metodę [retourSimulateur] klasy [Form]. Przedstawiony formularz wprowadzania danych musi być pusty, tak jak powyżej.
12.5.7. Akcja [voirSimulations]
Kod JSF dla linku [voirSimulations] wygląda następująco:
<h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>
Akcja [voirSimulations] powiązana z linkiem umożliwia użytkownikowi wyświetlenie tabeli symulacji, niezależnie od stanu wprowadzonych danych:

Uzyskany wynik:

Zadanie: napisać metodę [voirSimulations] klasy [Form].
Należy zadbać o to, aby w przypadku, gdy lista symulacji jest pusta, wyświetlany widok to [vueSimulationsVides]:

Aby wyświetlić powyższy widok, należy użyć następującej strony [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. Akcja [retirerSimulation]
Użytkownik może usunąć symulacje ze swojej listy:

Otrzymany wynik jest następujący:

Jeśli z powyższej listy usuniemy ostatnią symulację, otrzymamy następujący wynik:

Kod JSF z kolumny [Retirer] w tabeli symulacji wygląda następująco:
<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>
- wiersz 5: link [Retirer] jest powiązany z metodą [retirerSimulation] klasy [Form]. Metoda ta wymaga podania numeru symulacji, którą należy usunąć. Numer ten jest przekazywany przez tag <f:setPropertyActionListener> w wierszu 8. Tag ten posiada dwa atrybuty: target i value; atrybut target wskazuje pole w modelu, do którego zostanie przypisana wartość atrybutu value. W tym przypadku numer symulacji, którą należy usunąć, #{simulation.num}, zostanie przypisany do pola numSimulationToDelete klasy [Form]:
// model widoków
...
private Integer numSimulationToDelete;
Gdy zostanie wykonana metoda [retirerSimulation] klasy [Form], będzie mogła wykorzystać wartość, która została wcześniej zapisana w polu numSimulationToDelete.
Zadanie: napisać metodę [retirerSimulation] klasy [Form].
12.5.9. Akcja [terminerSession]
Kod JSF powiązania [Terminer la session] wygląda następująco:
<h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['form.menu.terminerSession']}" action="#{form.terminerSession}" rendered="#{sessionData.menuTerminerSessionIsRendered}"/>
Akcja [terminerSession] powiązana z linkiem pozwala użytkownikowi zakończyć sesję i powrócić do pustego formularza:


Jeśli użytkownik posiadał listę symulacji, zostaje ona wyczyszczona. Ponadto numeracja symulacji zaczyna się od nowa od 1.
Zadanie: napisać metodę [terminerSession] klasy [Form].
12.6. Włączenie warstwy internetowej do architektury trójwarstwowej JSF / EJB
Poprzednia architektura aplikacji internetowej wyglądała następująco:
![]() |
Zastępujemy symulowaną warstwę [métier] warstwami [métier, DAO, jpa] zaimplementowanymi przez EJB w punkcie 7.1:
![]() |
Zadanie praktyczne: przeprowadzić integrację warstw JSF i EJB zgodnie z metodologią opisaną w punkcie 11.
![]() |














