Skip to content

12. Версія 7 — Веб-додаток PAM з кількома видами/сторінками

Тут ми повертаємося до початкової архітектури, в якій імітувався рівень [métier]. Тепер ми знаємо, що його можна легко замінити реальним рівнем [métier]. Імітований рівень [métier] полегшує тестування.

Додаток JSF належить до типу MVC (Модель-Вигляд-Контролер):

  • сервлет [Faces Servlet] є загальним контролером, що надається JSF. Цей контролер розширюється за допомогою обробників подій, специфічних для додатка. Обробники подій, з якими ми стикалися до цього моменту, були методами класів, що слугували шаблонами для сторінок JSF
  • сторінки JSF надсилають відповіді до клієнтського браузера. Це — представлення додатка.
  • Сторінки JSF містять динамічні елементи, які називаються шаблоном сторінки. Нагадаємо, що, на думку деяких авторів, модель охоплює об’єкти, з якими працює додаток, такі як, наприклад, класи FeuilleSalaire або Employe. Щоб розрізнити ці дві моделі, можна говорити про модель додатка та модель сторінки JSF.

В архітектурі JSF перехід зі сторінки JSFi на сторінку JSFj може бути проблематичним.

  • Була відображена сторінка JSFi. З цієї сторінки користувач ініціює POST за допомогою будь-якої події [1]
  • у JSF, цей POST, як правило, обробляється [2a,2b] методом C моделі Mi сторінки JSFi. Можна сказати, що метод C є вторинним контролером.
  • якщо за результатами виконання цього методу має відобразитися сторінка JSFj, контролер C повинен:
  1. оновити [2c] у моделі Mj сторінки JSFj
  2. повернути головному контролеру [2a] — ключ навігації, який дозволить відобразити сторінку JSFj

На етапі 1 необхідно, щоб шаблон Mi сторінки JSFi містив посилання на шаблон Mj сторінки JSFj. Це дещо ускладнює ситуацію, роблячи шаблони Mi взаємозалежними. Адже менеджер C шаблону Mi, який оновлює шаблон Mj, повинен знати про існування останнього. Якщо доведеться змінювати модель Mj, то доведеться змінювати й менеджер C моделі Mi.

Існує випадок, коли взаємозалежність моделей між собою можна уникнути: це коли є єдина модель M, яка використовується для всіх сторінок JSF. Ця архітектура може використовуватися лише в додатках, що мають лише кілька представлень, але в такому разі вона виявляється дуже простою у використанні. Саме її ми зараз і використовуємо.

У цьому контексті архітектура додатка виглядає наступним чином:

12.1. Види додатка

Користувачеві будуть представлені такі види:

  • екран [VueSaisies], що відображає форму моделювання

  • екран [VueSimulation], що використовується для відображення детальних результатів моделювання:

Image

Image

  • вигляд [VueSimulations], що містить перелік моделювань, виконаних клієнтом

Image

  • вікно [VueSimulationsVides], яке вказує, що у клієнта немає або більше немає моделювань:

Image

  • перегляд [VueErreur], який вказує на одну або кілька помилок:

Image

12.2. Проект NetBeans для шару [web]

Проект NetBeans для цієї версії буде таким проектом Maven:

  • у [1] — файли конфігурації,
  • у [2] — сторінки, у JSF,
  • у [3] — таблицю стилів та фонове зображення візуалізацій,
  • у [4] — класи шару [web],
  • у [5] — нижні шари додатка,
  • у [6] — файл повідомлень для інтернаціоналізації додатка,
  • у [7] — залежності проєкту.

Розглянемо деякі з цих елементів.

12.2.1. Конфігураційні файли

Файли [web.xml] та [faces-config.xml] ідентичні тим, що були у попередньому проєкті, за винятком однієї деталі у файлі [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>
  • рядки 4–6: головна сторінка — це сторінка [saisie.xhtml]

12.2.2. Таблиця стилів

Файл [styles.css] має такий вигляд:


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

Ось приклади коду JSF, у якому використовуються ці стилі:

Вигляд «Симуляції»


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

Отриманий результат такий:

Image

Вигляд помилки


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

Image

12.2.3. Файл повідомлень

Файл повідомлень [messages_fr.properties] має такий вигляд:


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. Шар [métier]

Модельований шар [métier] змінено таким чином:


package metier;

...
public class Metier implements ImetierLocal, Serializable {
  
  // список співробітників
  private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
  private List<Employe> listEmployes;
  
  // отримати платіжну відомість
  public FeuilleSalaire calculerFeuilleSalaire(String SS,
    double nbHeuresTravaillées, int nbJoursTravaillés) {
    // отримати дані про працівника
    Employe e=hashEmployes.get(SS);
    // повертає виняток, якщо працівника не існує
    if(e==null){
      throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
    }
    // повертає фіктивну платіжну відомість
    return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),e.getIndemnite(),new ElementsSalaire(100,100,100,100,100));
  }
  
  // список співробітників
  public List<Employe> findAllEmployes() {
    if(listEmployes==null){
      // створюється список із трьох працівників
      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)));
      // словник співробітників
      for(Employe e:listEmployes){
        hashEmployes.put(e.getSS(),e);
      }
      // додаємо співробітника, якого немає у списку
      listEmployes.add(new Employe("X","Y","Z","La brûlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12)));
    }
    // видача списку співробітників
    return listEmployes;
  }
}
  • рядок 8: список співробітників
  • рядок 7: той самий список у вигляді словника, індексованого за номером SS співробітників
  • рядки 24–39: метод findAllEmployes, який повертає список співробітників. Цей метод створює статичний список і посилається на поле employés з рядка 8.
  • рядки 27–33: створюються список та словник із двома працівниками
  • рядок 35: співробітник додається до списку співробітників (рядок 8), але не до словника hashEmployes (рядок 7). Це зроблено для того, щоб він з’явився у списку співробітників, але потім не був розпізнаний методом calculerFeuilleSalaire (рядок 14), щоб цей метод викликав виняток (рядок 17).
  • рядки 11–21: метод calculerFeuilleSalaire
  • рядок 14: співробітника шукають у словнику hashEmployes за його номером SS. Якщо його не знайдено, генерується виняток (рядки 16–18). Таким чином, ми отримаємо виняток для працівника з номером SS X, доданого в рядку 35 до списку працівників, але відсутнього у словнику hashEmployes.
  • У рядку 20 створюється та повертається фіктивна платіжна відомість.

12.3. Bean-и додатка

Буде три біни з трьома різними областями дії:

  

12.3.1. Bean ApplicationData

Бін ApplicationData матиме область дії 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 {

  // бізнес-шар
  private IMetierLocal metier = new Metier();
  // реєстратор
  private boolean logsEnabled = true;
  private final Logger logger = Logger.getLogger("pam");

  public ApplicationData() {
  }

  @PostConstruct
  public void init() {
    // журнал
    if (isLogsEnabled()) {
      logger.info("ApplicationData");
    }
  }

  // методи getter та setter
...
}
  • рядок 11: анотація @Named робить клас керованим біном. Слід зауважити, що, на відміну від попереднього проєкту, тут не використовується анотація @ManagedBean. Причина полягає в тому, що посилання на цей клас має бути введено в інший клас за допомогою анотації @Inject, а ця анотація вводить лише класи з анотацією @Named.
  • рядок 12: анотація @ApplicationScoped робить цей клас об’єктом з областю дії «додаток». Зазначимо, що клас анотації — це [javax.enterprise.context.ApplicationScoped] (рядок 6), а не [javax.faces.bean.ApplicationScoped], як у бінах попереднього проєкту.

Бін ApplicationData виконує дві функції:

  • рядок 16: збереження посилання на шар [métier],
  • рядки 18–19: визначення логера, який інші біни зможуть використовувати для ведення журналів у консолі GlassFish.

12.3.2. Бін SessionData

Bean SessionData матиме сесійну область дії:


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 {

  // додаток
  @Inject
  private ApplicationData applicationData;
  // симуляції
  private List<Simulation> simulations = new ArrayList<Simulation>();
  private int numDerniereSimulation = 0;
  private Simulation simulation;
  // меню
  private boolean menuFaireSimulationIsRendered = true;
  private boolean menuEffacerSimulationIsRendered = true;
  private boolean menuEnregistrerSimulationIsRendered;
  private boolean menuVoirSimulationsIsRendered;
  private boolean menuRetourSimulateurIsRendered;
  private boolean menuTerminerSessionIsRendered = true;
  // локаль
  private String locale="fr_FR";
  
  // конструктор
  public SessionData() {
  }

  @PostConstruct
  public void init() {
    // журнал
    if (applicationData.isLogsEnabled()) {
      applicationData.getLogger().info("SessionData");
    }
  }

  // управління меню
  public void setMenu(boolean menuFaireSimulationIsRendered, boolean menuEnregistrerSimulationIsRendered, boolean menuEffacerSimulationIsRendered, boolean menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean menuTerminerSessionIsRendered) {
    this.setMenuFaireSimulationIsRendered(menuFaireSimulationIsRendered);
    this.setMenuEnregistrerSimulationIsRendered(menuEnregistrerSimulationIsRendered);
    this.setMenuVoirSimulationsIsRendered(menuVoirSimulationsIsRendered);
    this.setMenuEffacerSimulationIsRendered(menuEffacerSimulationIsRendered);
    this.setMenuRetourSimulateurIsRendered(menuRetourSimulateurIsRendered);
    this.setMenuTerminerSessionIsRendered(menuTerminerSessionIsRendered);
  }

  // методи getter та setter
  ...
}
  • рядок 13: клас SessionData є керованим біном (@Named), який можна буде вводити в інші керовані біни,
  • рядок 14: він має сесійну область дії (@SessionScoped),
  • рядки 18–19: у нього вводиться посилання на bean ApplicationData (@Inject),
  • рядки 21–32: дані додатка, які мають зберігатися протягом сеансів.
  • рядок 21: список симуляцій, виконаних користувачем,
  • рядок 22: номер останнього збереженого моделювання,
  • рядок 23: останнє виконане моделювання,
  • рядки 25–30: опції меню,
  • рядок 32: локаль додатка.

Рядки 39–44: метод init виконується після інстанціювання класу (@PostConstruct). Тут він використовується лише для того, щоб залишити слід свого виконання. Потрібно переконатися, що він виконується лише один раз на користувача, оскільки клас має сесійну область дії. Рядок 42: метод використовує логер, визначений у класі ApplicationData. Саме тому потрібно було ввести посилання на цей бін (рядки 18–19).

12.3.3. Бін Form

Бін Form має область дії 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() {
  }
  // інші біни
  @Inject
  private ApplicationData applicationData;
  @Inject
  private SessionData sessionData;
  // модель представлень
  private String comboEmployesValue = "";
  private String heuresTravaillées = "";
  private String joursTravaillés = "";
  private Integer numSimulationToDelete;
  private List<Erreur> erreurs = new ArrayList<Erreur>();
  private FeuilleSalaire feuilleSalaire;

  
  // список співробітників
  public List<Employe> getEmployes(){
    return applicationData.getMetier().findAllEmployes();
  }
  
  // дія меню
  public String faireSimulation() {
    ...
  }

  public String enregistrerSimulation() {
  ...
  }

  public String effacerSimulation() {
  ...
  }

  public String voirSimulations() {
  ...
  }

  public String retourSimulateur() {
   ...
  }

  public String terminerSession() {
  ...
  }

  public String retirerSimulation() {
 ...
  }

  private void razFormulaire() {
 ...
  }

// геттери та сеттери
...
}
  • у рядку 17 клас є керованим біном (@Named),
  • у рядку 18 — з областю дії запиту (@RequestScoped),
  • рядки 24–25: ін'єкція посилання на bean з областю дії «додаток» ApplicationData,
  • рядки 26–27: ін'єкція посилання на бін з областю дії сесії SessionData.

12.4. Сторінки додатка

  

12.4.1. [layout.xhtml]

Сторінка [layout.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">

  <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(){
        // зміна значень, що передаються
        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">
        <!-- заголовок -->
        <ui:include src="entete.xhtml" />
        <!-- вміст -->
        <ui:insert name="part1" >
          Gestion des assistantes maternelles
        </ui:insert>
        <ui:insert name="part2"/>
      </h:form>
    </h:body>
  </f:view>
</html>

Кожен вигляд складається з таких елементів:

  • заголовка, що відображається фрагментом [entete.xhtml] (рядок 24),
  • основну частину, що складається з двох фрагментів, названих part1 (рядки 26–28) та part2 (рядок 29),
  • рядок 8: атрибут locale забезпечує інтернаціоналізацію сторінок. Тут буде лише один: fr_FR,
  • рядки 14–19: код JavaScript.

Сторінка [layout.xhtml] відображається наступним чином:

  • у [1] — заголовок [entete.xhtml],
  • у [2] — фрагмент part1.

12.4.2. Заголовок [entete.xhtml]

Код сторінки [entete.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>
    <!-- заголовок -->
    <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>
  • рядок 12: заголовок додатка,
  • рядки 16–21: шість посилань, що відповідають шести діям, які може виконати користувач. Ці посилання керуються (атрибут rendered) булевими значеннями з біна SessionData,
  • рядок 17: клік на посилання [Effacer la simulation] викликає виконання функції JavaScript raz. Ця функція була визначена в моделі [layout.xhtml],
  • рядок 18: атрибут immediate=true призводить до того, що правильність даних не перевіряється перед виконанням методу [Form].enregistrerSimulation. Це зроблено навмисно. Може виникнути потреба зберегти останнє моделювання, виконане хвилину тому, навіть якщо дані, введені з того часу у форму (але ще не перевірені), є недійсними. Те саме стосується дій [Voir les simulations], [Effacer la simulation], [Retour au simulateur] та [Terminer la session].

12.5. Варіанти використання додатка

12.5.1. Відображення головної сторінки

Головна сторінка — це така сторінка [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>
  • рядок 8: вона відображається всередині сторінки [layout.xhtml],
  • рядок 9: на місці фрагмента з назвою part1. У цьому фрагменті відображається сторінка [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>

Цей код відображає таке зображення:

 

Завдання: доповніть рядок 13 коду XHTML. Список елементів комбінованого списку співробітників надається методом біна [Form]. Напишіть цей метод. Елементи, що відображаються у комбінованому списку, матимуть властивість itemValue, що дорівнює номеру SS співробітника, а властивість itemLabel буде рядком, що складається з імені та прізвища цього співробітника.



Питання: як слід ініціалізувати bean [SessionData], щоб під час початкового запиту GET, що надсилається до форми, меню заголовка відповідало наведеному вище?


12.5.2. Дія [faireSimulation]

Код JSF дії [faireSimulation] має такий вигляд:


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

Операція [faireSimulation] розраховує розрахунковий лист:

Image

На основі попередньої сторінки отримуємо такий результат:

Результати моделювання відображаються на наступній сторінці [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>
  • у рядку 8 сторінка [simulation.xhtml] вставляється у сторінку [layout.xhtml],
  • рядки 9–11: область введення даних відображається у фрагменті part1 сторінки макета,
  • рядки 12–91: симуляція відображається у фрагменті part2 макета сторінки.

Завдання: написати метод [faireSimulation] класу [Form]. Моделювання буде збережено в біні SessionData.


12.5.3. Обробка помилок

Потрібно забезпечити належне оброблення винятків, які можуть виникнути під час розрахунку симуляції. Для цього у коді методу [faireSimulation] буде використано конструкцію try / catch:


  // дія меню
  public String faireSimulation(){
    try{
      // розраховується розрахунковий лист
      feuilleSalaire= ...
      // відображення симуляції
      ...
      // оновлення меню
      ...
       // повернення до режиму моделювання
      return "simulation";
    }catch(Throwable th){
      // очищення списку попередніх помилок
      ...
      // створюється новий список помилок
      ...
      // відображається вікно vueErreur
      ...
      // оновлення меню
      ...
       // відображається вікно помилки
      return "erreurs";
    }
}

Список помилок, створений у рядку 15, є таким:


  // шаблон переглядів
  ...
  private List<Erreur> erreurs=new ArrayList<Erreur>();
...

Клас «Помилка» визначено так:


package web.entities;

public class Erreur {
  
  public Erreur() {
  }
  
  // поле
  private String classe;
  private String message;

  // конструктор
  public Erreur(String classe, String message){
    this.setClasse(classe);
    this.message=message;
  }
  
  // методи getter та setter
...  
}

Помилки будуть винятками, ім’я класу яких зберігається у полі «клас», а повідомлення — у полі «повідомлення».

Список помилок, сформований у методі [faireSimulation], складається з:

  • початкового винятку th типу Throwable, що виник,
  • її причини th.getCause(), якщо вона є,
  • причини причини h.getCause().getCause(), якщо вона існує.
  • ...

Ось приклад списку помилок:

Image

У наведеному вище прикладі співробітник [Z Y] відсутній у словнику співробітників, який використовує імітований рівень [métier]. У цьому випадку імітований рівень [métier] генерує виняток (рядок 6 нижче):


  public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
    // отримуємо співробітника
    Employe e=hashEmployes.get(SS);
    // видаємо виняток, якщо співробітник не існує
    if(e==null){
      throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
    }
...
}

Отримано такий результат:

Image

Цей вигляд відображається на наступній сторінці [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>

Завдання: доповнити метод [faireSimulation] таким чином, щоб у разі виникнення винятку він відображав представлення [vueErreur].


12.5.4. Дія [effacerSimulation]

Дія [effacerSimulation] дозволяє користувачеві відкрити порожню форму:

Код JSF посилання [effacerSimulation] має такий вигляд:


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

Клік на посилання [EffacerSimulation] спочатку викликає функцію JavaScript raz(). Цей метод визначено на сторінці [layout.xhtml]:


    <script type="text/javascript">
      function raz(){
        // зміна значень, що передаються
        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>

У рядках 4–6 змінюються передані значення. Слід зауважити, що

  • передані значення є дійсними, c.a.d, і що вони пройдуть перевірку на валідність полів введення heuresTravaillees та joursTravailles.
  • функція raz не відправляє форму. Адже вона буде відправлена за посиланням cmdEffacerSimulation. Ця операція post відбудеться після виконання функції JavaScript raz.

Під час виконання функції post відправлені значення пройдуть звичайний шлях: перевірка, а потім присвоєння полям моделі. У класі [Form] це такі поля:


  // модель подання
  private String comboEmployesValue;
  private String heuresTravaillées;
  private String joursTravaillés;
  ...

У ці три поля будуть записані три значення {"0","0","0"}. Після цього буде виконано метод effacerSimulation.


Завдання: написати метод [effacerSimulation] класу [Form]. Необхідно забезпечити, щоб:

  • відображалася лише область введення даних,

  • комбінований список був наведений на перший елемент,

  • поля введення heuresTravaillees та joursTravailles відображали порожні рядки.


12.5.5. Дія [enregistrerSimulation]

Код JSF посилання [enregistrerSimulation] має такий вигляд:


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

Дія [enregistrerSimulation], пов’язана з посиланням, дозволяє зберегти поточну симуляцію у списку симуляцій, що зберігається у класі [SessionData]:


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

Клас « : Симуляція» має такий вигляд:


package web.entities;

import metier.FeuilleSalaire;

public class Simulation {
  
  public Simulation() {
  }

  // поля симуляції
  private Integer num;
  private FeuilleSalaire feuilleSalaire;
  private String heuresTravaillées;
  private String joursTravaillés;
  
  // конструктор
  public Simulation(Integer num,String heuresTravaillées, String joursTravaillés, FeuilleSalaire feuilleSalaire){
    this.setNum(num);
    this.setFeuilleSalaire(feuilleSalaire);
    this.setHeuresTravaillées(heuresTravaillées);
    this.setJoursTravaillés(joursTravaillés);
  }
  
  public double getIndemnites(){
    return feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+ feuilleSalaire.getElementsSalaire().getIndemnitesRepas();
  }

  // методи getter та setter
...
}

Цей клас дозволяє зберегти симуляцію, виконану користувачем:

  • рядок 11: номер симуляції,
  • рядок 12: розрахункова відомість,
  • рядок 13: кількість відпрацьованих годин,
  • рядок 14: кількість відпрацьованих днів.

Ось приклад запису:

Image

На основі попередньої сторінки отримуємо такий результат:

Image

Номер симуляції — це число, яке збільшується з кожним новим записом. Воно належить до bean SessionData:


  // симуляції
  private List<Simulation> simulations = new ArrayList<Simulation>();
  private int numDerniereSimulation = 0;
private Simulation simulation;
  • рядок 2: номер останнього виконаного моделювання.

Метод [enregistrerSimulation] може діяти таким чином:

  • отримати номер останнього моделювання з біна [SessionData] та збільшити його,
  • додати нове моделювання до списку моделювань, що ведеться класом [SessionData],
  • вивести таблицю симуляцій:

Таблиця симуляцій відображається на сторінці [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">
      <!-- таблиця моделювань -->
      <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>
  • у рядку 8 сторінка [simulations.xhtml] вставляється всередину сторінки [layout.xhtml],
  • у рядку 9 замість фрагмента з назвою part1,
  • у рядку 11 тег <h:dataTable> використовує поле #{sessionData.simulations} як джерело даних, c.a.d. Наступне поле:

  // моделювання
private List<Simulation> simulations;
  • атрибут var="simulation" визначає ім'я змінної, що представляє поточну симуляцію всередині тегу <h:datatable>

  • атрибут headerClass="simulationsHeaders" визначає стиль заголовків стовпців таблиці.

  • атрибут columnClasses="...." визначає стиль кожного стовпця таблиці

Розглянемо один із стовпців таблиці та подивимося, як він побудований:

Код JSF стовпця Nom має такий вигляд:


            <h:column>
              <f:facet name="header">
                <h:outputText value="#{msg['simulations.headers.nom']}"/>
              </f:facet>
              <h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
</h:column>
  • рядки 2–4: тег <f:facet name="header"> визначає заголовок стовпця
  • рядок 5: ім’я працівника вказано так:
    • simulation посилається на атрибут var тегу <h:dataTable ...>:

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

«simulation» позначає поточну симуляцію зі списку симуляцій: спочатку першу, потім другу, …

  • (продовження)
    • simulation.feuilleSalaire посилається на поле feuilleSalaire поточної симуляції
    • simulation.feuilleSalaire.employe посилається на поле «працівник» поля feuilleSalaire
    • simulation.feuilleSalaire.employe.nom посилається на поле «nom» поля «employe»

Ця ж процедура повторюється для всіх стовпців таблиці. Складність виникає зі стовпцем «Indemnités», який генерується за допомогою такого коду:


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

У рядку 5 відображається значення simulation.indemnites. Однак клас Simulation не має поля indemnites. Тут слід пам’ятати, що поле indemnites використовується не безпосередньо, а через метод simulation.getIndemnites(). Отже, достатньо, щоб цей метод існував. Поле indemnites, у свою чергу, може не існувати. Метод getIndemnites повинен повертати суму надбавок працівника. Це вимагає проміжного обчислення, оскільки ця сума безпосередньо не вказана у відомості про заробітну плату. Метод getIndemnites наведено в розділі 12.5.5.


Завдання: написати метод [enregistrerSimulation] класу [Form].


12.5.6. Дія [retourSimulateur]

Код JSF для зв’язку [retourSimulateur] має такий вигляд:


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

Дія [retourSimulateur], пов’язана з посиланням, дозволяє користувачеві повернутися з перегляду [vueSimulations] до перегляду [vueSaisies]:

Image

Отриманий результат:

Image


Завдання: написати метод [retourSimulateur] класу [Form]. Представлена форма введення даних має бути порожньою, як показано вище.


12.5.7. Дія [voirSimulations]

Код JSF для посилання [voirSimulations] має такий вигляд:


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

Дія [voirSimulations], пов’язана з посиланням, дозволяє користувачеві отримати таблицю моделювань незалежно від стану його введених даних:

Image

Отриманий результат:

Image


Завдання: написати метод [voirSimulations] класу [Form].


Потрібно забезпечити, щоб у разі, якщо список симуляцій порожній, відображався вигляд [vueSimulationsVides]:

Image

Для відображення вищезазначеного виду будемо використовувати таку сторінку [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. Дія [retirerSimulation]

Користувач може видалити симуляції зі свого списку:

Image

Отриманий результат такий:

Image

Якщо з наведеного вище списку видалити останню симуляцію, отримаємо такий результат:

Image

Код JSF у стовпці [Retirer] таблиці симуляцій має такий вигляд:


          <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>
  • рядок 5: посилання [Retirer] пов’язане з методом [retirerSimulation] класу [Form]. Цьому методу потрібно знати номер симуляції, яку потрібно видалити. Він надається тегом <f:setPropertyActionListener> у рядку 8. Цей тег має два атрибути — target та value: атрибут target вказує на поле моделі, якому буде присвоєно значення атрибута value. У даному випадку номер симуляції, яку потрібно видалити, #{simulation.num}, буде присвоєно полю numSimulationToDelete класу [Form]:

  // модель переглядів
  ...
  private Integer numSimulationToDelete;

Коли буде виконуватися метод [retirerSimulation] класу [Form], він зможе використовувати значення, яке було раніше збережено в полі numSimulationToDelete.


Завдання: написати метод [retirerSimulation] класу [Form].


12.5.9. Дія [terminerSession]

Код JSF для посилання [Terminer la session] має такий вигляд:


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

Дія [terminerSession], пов’язана з посиланням, дозволяє користувачеві вийти з сесії та повернутися до порожньої форми введення даних:

Image

Image

Якщо у користувача був список симуляцій, він очищується. Крім того, нумерація симуляцій починається знову з 1.


Завдання: написати метод [terminerSession] класу [Form].


12.6. Інтеграція веб-шару в тришарову архітектуру JSF / EJB

Архітектура попереднього веб-додатку була такою:

Ми замінюємо імітований рівень [métier] на рівні [métier, DAO, jpa], реалізовані за допомогою EJB у розділі 7.1:


Практичне завдання: здійснити інтеграцію шарів JSF та EJB, дотримуючись методології, викладеної в розділі 11.