Skip to content

12. نسخه ۷ – اپلیکیشن وب چندنمای/چندصفحه‌ای PAM

در اینجا به معماری اولیه بازمی‌گردیم که در آن لایه [métier] شبیه‌سازی شده بود. اکنون می‌دانیم که این لایه می‌تواند به راحتی با لایه واقعی [métier] جایگزین شود. لایه شبیه‌سازی‌شده [métier] انجام تست را تسهیل می‌کند.

یک برنامه کاربردی JSF از نوع MVC (مدل-نما-کنترل‌کننده) است:

  • سرولت [Faces Servlet] کنترل‌کنندهٔ عمومی ارائه‌شده توسط JSF است. این کنترل‌کننده توسط دستگیرکننده‌های رویداد خاص برنامه توسعه داده می‌شود. رویدادپردازهایی که تاکنون با آن‌ها مواجه شده‌ایم، متدهایی از کلاس‌هایی هستند که به‌عنوان الگو برای صفحات JSF عمل می‌کنند.
  • صفحات JSF پاسخ‌ها را به مرورگر کلاینت ارسال می‌کنند. این‌ها نماهای برنامه هستند.
  • صفحات JSF شامل عناصر پویایی هستند که به آن‌ها مدل صفحه گفته می‌شود. شایان ذکر است که، از نظر برخی نویسندگان، مدل شامل اشیایی است که توسط برنامه مدیریت می‌شوند، مانند، برای مثال، کلاس‌های FeuilleSalaire یا Employe. برای تمایز قائل شدن بین این دو مدل، می‌توان به مدل برنامه و مدل یک صفحه JSF اشاره کرد.

در معماری JSF، ناوبری از یک صفحه JSFi به یک صفحه JSFj می‌تواند مشکل‌ساز باشد.

  • صفحه JSFi نمایش داده شده است. از این صفحه، کاربر از طریق یک رویداد [1] یک POST را فعال می‌کند
  • در JSF؛ این POST معمولاً توسط یک متد C از مدل Mi صفحه JSFi مدیریت می‌شود. می‌توان گفت که این متد C یک کنترل‌کننده ثانویه است.
  • اگر در پایان این روش صفحه JSFj قرار است نمایش داده شود، کنترل‌کننده C باید:
  1. [2c]، قالب Mj برای صفحه JSFj را به‌روزرسانی کند
  2. [2a] را به کنترل‌کننده اصلی بازگرداند، کلید ناوبری که نمایش صفحه JSFj را امکان‌پذیر می‌سازد

مرحله ۱ مستلزم آن است که قالب 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>
  • خطوط ۴–۶: صفحهٔ اصلی صفحهٔ [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;
  }
}
  • خط ۸: فهرست کارمندان
  • خط ۷: همان لیست به شکل یک دیکشنری که با اعداد SS کارمندان نمایه شده است
  • خطوط ۲۴–۳۹: متد findAllEmployes که لیست کارمندان را بازمی‌گرداند. این متد یک لیست سخت‌کدشده ایجاد می‌کند و از طریق فیلد employés در خط ۸ به آن ارجاع می‌دهد.
  • خطوط ۲۷–۳۳: یک لیست و یک دیکشنری حاوی دو کارمند ایجاد می‌شوند
  • خط ۳۵: یک کارمند به لیست کارمندان (خط ۸) اضافه می‌شود اما به دیکشنری hashEmployes (خط ۷) اضافه نمی‌شود. این کار برای این است که آن‌ها در لیست کشویی «کارمندان» ظاهر شوند اما در ادامه توسط متد calculerFeuilleSalaire (خط 14) شناسایی نشوند، که باعث ایجاد یک استثنا (خط 17) می‌شود.
  • خطوط ۱۱–۲۱: متد calculerFeuilleSalaire
  • خط 14: کارمند با استفاده از شناسه SS در فرهنگ لغت hashEmployes جستجو می‌شود. اگر پیدا نشود، یک استثنا پرتاب می‌شود (خطوط 16–18). بنابراین، یک استثنا برای کارمندی با شناسه SS X که در خط ۳۵ به فهرست کارمندان اضافه شده اما به فرهنگ لغت hashEmployes اضافه نشده است، ایجاد خواهد شد.
  • در خط ۲۰، یک فیش حقوقی آزمایشی ایجاد و بازگردانده می‌شود.

12.3. بیان‌های برنامه

سه بیِن با سه دامنهٔ متفاوت وجود خواهند داشت:

  

12.3.1. دانه 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");
    }
  }

  // گیرنده و تنظیم‌کننده
...
}
  • خط ۱۱: تگ @Named این کلاس را به یک bean مدیریت‌شده تبدیل می‌کند. توجه کنید که برخلاف پروژه قبلی، از anotation @ManagedBean استفاده نشده است. دلیل این امر آن است که یک مرجع به این کلاس باید با استفاده از anotation @Inject به کلاسی دیگر تزریق شود، و این anotation تنها کلاس‌هایی را تزریق می‌کند که دارای anotation @Named هستند.
  • خط ۱۲: anotation @ApplicationScoped این کلاس را به یک شی در مقیاس برنامه (application-scoped) تبدیل می‌کند. توجه داشته باشید که نام کلاس در این anotation، [javax.enterprise.context.ApplicationScoped] (خط ۶) است و نه [javax.faces.bean.ApplicationScoped] مانند beans پروژه قبلی.

بیان ApplicationData دو هدف را برآورده می‌کند:

  • خط 16: برای حفظ یک مرجع به لایه [métier
  • خطوط ۱۸–۱۹: تعریف یک لاگر که سایر بین‌ها می‌توانند برای ثبت لاگ در کنسول GlassFish از آن استفاده کنند.

12.3.2. بیان SessionData

بیان 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);
  }

  // گیرنده‌ها و تنظیم‌کننده‌ها
  ...
}
  • خط ۱۳: کلاس SessionData یک bean مدیریت‌شده (@Named) است که می‌تواند به سایر beanهای مدیریت‌شده تزریق شود،
  • خط ۱۴: این دارای دامنه جلسه است (@SessionScoped),
  • خطوط ۱۸–۱۹: مرجعی به بیون ApplicationData به آن تزریق می‌شود (@Inject
  • خطوط ۲۱–۳۲: داده‌های کاربردی که باید در طول جلسات حفظ شوند.
  • خط ۲۱: فهرست شبیه‌سازی‌های انجام‌شده توسط کاربر،
  • خط ۲۲: شماره آخرین شبیه‌سازی ذخیره شده،
  • خط ۲۳: آخرین شبیه‌سازی انجام‌شده،
  • خطوط ۲۵–۳۰: گزینه‌های منو،
  • خط ۳۲: لوکال برنامه.

خطوط ۳۹–۴۴: متد init پس از instantiate شدن کلاس (@PostConstruct) اجرا می‌شود. در اینجا، این متد صرفاً برای ثبت اجرای خود استفاده می‌شود. ما باید بتوانیم تأیید کنیم که این متد فقط یک بار برای هر کاربر اجرا می‌شود، زیرا این کلاس دارای دامنه جلسه (session scope) است. خط ۴۲: این متد از لاگر (logger) تعریف‌شده در کلاس ApplicationData استفاده می‌کند. به همین دلیل بود که نیاز داشتیم یک مرجع به این بین (bean) را تزریق کنیم (خطوط ۱۸–۱۹).

12.3.3. بیان فرم

بیان فرم دارای دامنه 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() {
 ...
  }

// گیرنده و تنظیم‌کننده
...
}
  • خط ۱۷، این کلاس یک bean مدیریت‌شده است (@Named
  • خط ۱۸، دارای دامنه درخواست (@RequestScoped) است،
  • خطوط ۲۴–۲۵: تزریق مرجعی به بین با دامنهٔ برنامه ApplicationData،
  • خطوط ۲۶–۲۷: تزریق مرجع به bean با دامنه جلسه 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) تشکیل شده است،
  • خط ۸: ویژگی locale بین‌المللی‌سازی صفحات را تضمین می‌کند. در اینجا تنها یکی وجود خواهد داشت: fr_FR،
  • خطوط 14–19: یک کد جاوااسکریپت.

صفحه [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>
  • خط ۱۲: عنوان برنامه،
  • خطوط ۱۶–۲۱: شش لینک متناظر با شش عملی که کاربر می‌تواند انجام دهد. این لینک‌ها توسط متغیرهای بولی در bean SessionData کنترل می‌شوند (`atribut rendered`)،
  • خط ۱۷: کلیک بر روی لینک [Effacer la simulation] باعث اجرای تابع جاوا اسکریپت `raz` می‌شود. این تابع در مدل [layout.xhtml] تعریف شده است،
  • خط ۱۸: ویژگی `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>
  • خط ۸: در داخل صفحه [layout.xhtml] نمایش داده می‌شود،
  • خط ۹: به جای قطعه با نام 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>

این کد نمای زیر را نمایش می‌دهد:

 

سؤال: خط ۱۳ کد 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>
  • خط ۸: صفحه [simulation.xhtml] در صفحه [layout.xhtml] درج می‌شود،
  • رده‌های ۹–۱۱: ناحیه ورودی در قطعه «part1» صفحه چیدمان نمایش داده می‌شود،
  • خطوط ۱۲–۹۱: شبیه‌سازی در قطعه «part2» صفحه چیدمان نمایش داده می‌شود.

سؤال: متد [faireSimulation] کلاس [Form] را بنویسید. شبیه‌سازی در بین SessionData ذخیره خواهد شد.


12.5.3. مدیریت خطا

ما می‌خواهیم بتوانیم هر استثنایی را که ممکن است در حین محاسبه یک شبیه‌سازی رخ دهد، به‌درستی مدیریت کنیم. برای این کار، کد متد [faireSimulation] از یک بلوک try/catch استفاده خواهد کرد:


  // عمل منو
  public String faireSimulation(){
    try{
      // محاسبه فیش حقوقی
      feuilleSalaire= ...
      // نمایش شبیه‌سازی
      ...
      // به‌روزرسانی منو
      ...
       //بازگشت به نمای شبیه‌سازی
      return "simulation";
    }catch(Throwable th){
      // پاک کردن فهرست خطاهای قبلی
      ...
      // فهرست جدید خطاها را ایجاد می‌کند
      ...
      //نمایش نما vueErreur
      ...
      // به‌روزرسانی منو
      ...
       //نمایش نمای خطا
      return "erreurs";
    }
}

فهرست خطاهای ایجاد شده در خط ۱۵ به شرح زیر است:


  //قالب نما
  ...
  private List<Erreur> erreurs=new ArrayList<Erreur>();
...

کلاس Error به صورت زیر تعریف شده است:


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;
  }
  
  // گیرنده و تنظیم‌کننده
...  
}

خطاها به صورت استثنا (exceptions) خواهند بود که نام کلاس آن‌ها در فیلد 'class' و پیام آن‌ها در فیلد 'message' ذخیره می‌شود.

فهرست خطاهای ساخته‌شده در متد [faireSimulation] شامل موارد زیر است:

  • استثناء اولیه از نوع Throwable که رخ داده است،
  • علت آن، th.getCause()، در صورت وجود،
  • علتِ علت h.getCause().getCause()، در صورت وجود.
  • ...

در اینجا مثالی از یک لیست خطا آورده شده است:

Image

در مثال بالا، کارمند [Z Y] در فرهنگ لغت کارمندان مورد استفاده لایه شبیه‌سازی [métier] وجود ندارد. در این حالت، لایه شبیه‌سازی [métier] یک استثنا پرتاب می‌کند (خط ۶ زیر):


  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] ابتدا باعث اجرای تابع جاوااسکریپت 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>

خطوط ۴–۶ مقادیر ارسال‌شده را تغییر می‌دهند. توجه کنید که

  • مقادیر ارسال‌شده معتبر هستند، c.a.d، و اینکه آنها بررسی‌های اعتبارسنجی برای فیلدهای ورودی heuresTravaillees و joursTravailles را پاس خواهند داد.
  • تابع raz فرم را ارسال نمی‌کند. این به این دلیل است که فرم از طریق لینک cmdEffacerSimulation ارسال خواهد شد. این post پس از اجرای تابع جاوا اسکریپت 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>();

کلاس « Simulation» به شرح زیر است:


package web.entities;

import metier.FeuilleSalaire;

public class Simulation {
  
  public Simulation() {
  }

  // fields in a 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();
  }

  // گیرنده و تنظیم‌کننده
...
}

این کلاس برای ذخیره شبیه‌سازی ایجاد شده توسط کاربر استفاده می‌شود:

  • خط ۱۱: شماره شبیه‌سازی،
  • خط ۱۲: برگه پرداخت که محاسبه شده است،
  • خط ۱۳: تعداد ساعات کاری،
  • خط ۱۴: تعداد روزهای کاری.

در اینجا مثالی از یک رکورد آورده شده است:

Image

از صفحه قبلی، نتیجه زیر را به دست می‌آوریم:

Image

شماره شبیه‌سازی با هر رکورد جدید افزایش می‌یابد. این مربوط به دانه SessionData است:


  // شبیه‌سازی‌ها
  private List<Simulation> simulations = new ArrayList<Simulation>();
  private int numDerniereSimulation = 0;
private Simulation simulation;
  • خط ۲: شماره آخرین شبیه‌سازی انجام‌شده.

روش [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>
  • در خط ۸، صفحه [simulations.xhtml] درون صفحه [layout.xhtml] درج می‌شود،
  • خط ۹، به جای قطعه با نام part1،
  • خط ۱۱، تگ <h:dataTable> از فیلد #{sessionData.simulations} به عنوان منبع داده‌های خود استفاده می‌کند، c.a.d. فیلد بعدی:

  //شبیه‌سازی‌ها
private List<Simulation> simulations;
  • ویژگی var="simulation" نام متغیری را که نمایانگر شبیه‌سازی فعلی است، در تگ <h:datatable> تنظیم می‌کند.

  • ویژگی headerClass="simulationsHeaders" سبک سربرگ‌های ستون جدول را تعیین می‌کند.

  • ویژگی columnClasses=&quot;....&quot; سبک هر یک از ستون‌های جدول را تعیین می‌کند

بیایید یکی از ستون‌های جدول را بررسی کنیم و ببینیم ساختار آن چگونه است:

کد 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>
  • رده‌های ۲–۴: تگ <f:facet name="header"> سربرگ ستون را تعریف می‌کند
  • خط ۵: نام کارمند به صورت زیر نوشته شده است:
    • 'simulation' به ویژگی 'var' تگ <h:dataTable ...> اشاره دارد:

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

'simulation' به شبیه‌سازی فعلی در فهرست شبیه‌سازی‌ها اشاره دارد: ابتدا شبیه‌سازی اول، سپس شبیه‌سازی دوم، …

  • (ادامه)
    • simulation.feuilleSalaire به فیلد feuilleSalaire در شبیه‌سازی جاری اشاره دارد
    • simulation.feuilleSalaire.employe به فیلد 'employee' از فیلد feuilleSalaire اشاره دارد
    • simulation.feuilleSalaire.employe.nom به فیلد «name» از فیلد «employee» اشاره دارد

همان تکنیک برای همه ستون‌های جدول تکرار می‌شود. مشکلی در ستون «Allowances» وجود دارد که با استفاده از کد زیر تولید می‌شود:


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

خط ۵ مقدار 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>
  • خط ۵: لینک [Retirer] با متد [retirerSimulation] از کلاس [Form] مرتبط است. این متد به شماره شبیه‌سازی که باید حذف شود نیاز دارد. این شماره توسط تگ <f:setPropertyActionListener> در خط ۸ فراهم می‌شود. این تگ دو ویژگی دارد: '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

اگر کاربر فهرستی از شبیه‌سازی‌ها داشته باشد، آن فهرست پاک می‌شود. علاوه بر این، شماره‌گذاری شبیه‌سازی‌ها به ۱ بازنشانی می‌شود.


سؤال: متد [terminerSession] را برای کلاس [Form] بنویسید.


12.6. ادغام لایه وب در معماری سه‌لایه JSF / EJB

معماری اپلیکیشن وب قبلی به شرح زیر بود:

ما لایه شبیه‌سازی‌شده [métier] را با لایه‌های [métier, DAO, jpa] که توسط EJB در بخش 7.1 پیاده‌سازی شده‌اند، جایگزین می‌کنیم:


تمرین عملی: لایه‌های JSF و EJB را مطابق روش‌شناسی ذکرشده در بند ۱۱ یکپارچه کنید.