Skip to content

9. برنامهٔ نمونهٔ 05: rdvmedecins-pfm-ejb

بیایید ساختار برنامه نمونه 01، JSF / EJB، که برای سرور GlassFish توسعه یافته است را به یاد بیاوریم:

ما هیچ تغییری در این معماری ایجاد نمی‌کنیم، به جز لایه وب که در اینجا با استفاده از JSF، PrimeFaces و PrimeFaces Mobile پیاده‌سازی خواهد شد. مرورگر هدف یک مرورگر موبایل خواهد بود.

ما دو برنامه را با استفاده از GlassFish توسعه داده‌ایم:

  • برنامه ۰۱ از JSF / EJB استفاده می‌کرد و رابط کاربری نسبتاً ساده‌ای داشت،
  • برنامه ۰۳ از PF / EJB استفاده می‌کرد و رابط کاربری غنی داشت.

به دلیل تعداد محدود کامپوننت‌های موجود در PrimeFaces Mobile، به رابط کاربری پایه‌ای برنامه 01 بازمی‌گردیم. علاوه بر این، ویوها باید بتوانند خود را با اندازه کوچکتر صفحه‌های نمایش موبایل تطبیق دهند.

9.1. نماها

برای آشنایی بیشتر، در اینجا چند تصویر از اجرای برنامه در شبیه‌ساز آیفون 4 آورده شده است:

  • در [1]، صفحه اصلی. توجه داشته باشید که این بار مجبور بودیم نام ماشین را مشخص کنیم (می‌توانیم آدرس آن را نیز وارد کنیم، IP)، زیرا با ماشین localhost، هیچ چیزی نمایش داده نمی‌شد،
  • در [2]، فهرست پزشکان،
  • برای [3]، تاریخ قرار ملاقات مورد نظر،
  • در [4]، دکمه مشاهده برنامه روز،
  • در [5]، نمای جدید بازه‌های زمانی خالی دکتر را نمایش می‌دهد،
  • در [6]، مجموعه‌ای از دکمه‌ها برای پیمایش تقویم،
  • در [7]، پیامی که کاربر را از نام دکتر و روز مطلع می‌سازد،
  • در [8]، یک بازه زمانی برای رزرو. ما این کار را انجام می‌دهیم،
  • در [9]، نمای انتخاب مشتری،
  • در [10]، پیامی برای یادآوری پزشک، روز و بازه زمانی قرار ملاقات به مشتری،
  • به [11]، منوی کشویی مشتری،
  • در [12]، دکمه تأیید،
  • در [13]، کلیک روی دکمه تأیید ما را به تقویم بازمی‌گرداند،
  • در [14]، زمان‌بندی اشغال‌شده اکنون رزرو شده است. اکنون این رزرو را حذف خواهیم کرد،
  • در [15]، ما همچنان در همان نما می‌مانیم،
  • اما در [16]، قرار ملاقات حذف شده است،

امکان تغییر زبان در صفحه اصلی وجود دارد: [17], [18], [19]:

در نهایت، یک نمای خطا ارائه شده است:

9.2. پروژه NetBeans

پروژه NetBeans به شرح زیر است:

  
  • [mv-rdvmedecins-ejb-dao-jpa]: پروژه EJB شامل لایه‌های [DAO] و [JPA] از مثال 01،
  • [mv-rdvmedecins-ejb-metier]: پروژه EJB برای لایه [métier] از مثال 01،
  • [mv-rdvmedecins-pfmobile]: پروژه در لایه [web] / PrimeFaces Mobile – جدید،
  • [mv-rdvmedecins-pfmobile-app-ear]: پروژه سازمانی برای استقرار برنامه روی سرور GlassFish – جدید.

9.3. پروژه سازمانی

پروژه سازمانی صرفاً برای استقرار سه ماژول [mv-rdvmedecins-ejb-dao-jpa]، [mv-rdvmedecins-ejb-metier] و [mv-rdvmedecins-pfmobile] روی سرور GlassFish استفاده می‌شود. پروژه NetBeans به شرح زیر است:

این پروژه صرفاً برای این سه وابستگی، [1]، وجود دارد که در فایل [pom.xml] به شرح زیر تعریف شده‌اند:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
...

  <groupId>istia.st</groupId>
  <artifactId>mv-rdvmedecins-pfmobile-app-ear</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>ear</packaging>

  <name>mv-rdvmedecins-pfmobile-app-ear</name>

...
    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>mv-rdvmedecins-ejb-dao-jpa</artifactId>
            <version>${project.version}</version>
            <type>ejb</type>
        </dependency>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>mv-rdvmedecins-ejb-metier</artifactId>
            <version>${project.version}</version>
            <type>ejb</type>
        </dependency>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>mv-rdvmedecins-pfmobile</artifactId>
            <version>${project.version}</version>
            <type>war</type>
        </dependency>
    </dependencies>
</project>
  • خطوط ۶–۹: آرتیفکت Maven برای پروژه سازمانی،
  • خطوط 14–33: سه وابستگی پروژه. به نوع هر یک از این موارد (خطوط 19، 25، 31) توجه کنید.

برای اجرای برنامه وب، باید این پروژه سازمانی را اجرا کنید.

9.4. پروژه وب PrimeFaces Mobile به شرح زیر است:

پروژه وب PrimeFaces Mobile به شرح زیر است:

  • در [1]، صفحات پروژه قرار دارند. صفحه [index.xhtml] تنها صفحه پروژه است. این شامل پنج نما است: [vue1.xhtml]، [vue2.xhtml]، [vue3.xhtml]، [vueErreurs.xhtml] و [config.xhtml
  • که [2] حاوی جاوا بین‌ها است. بین [Application] دارای دامنه application است، در حالی که بین [Form] دارای دامنه session است. کلاس [Erreur] یک خطا را در بر می‌گیرد،
  • در [3]، فایل‌های پیام برای بین‌المللی‌سازی،
  • در [4]، وابستگی‌ها. پروژه وب به پروژه EJB در لایه [DAO] وابسته است، پروژه EJB در لایه [métier]، و PrimeFaces Mobile برای لایه [web].

9.5. پیکربندی پروژه

پیکربندی پروژه مشابه پیکربندی پروژه‌های PrimeFaces یا JSF است که بررسی کرده‌ایم. ما فایل‌های پیکربندی را بدون توضیح مجدد فهرست می‌کنیم.

 

[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">
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
  <context-param>
    <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
    <param-value>true</param-value>
  </context-param> 
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>
      30
    </session-timeout>
  </session-config>
  <welcome-file-list>
    <welcome-file>faces/index.xhtml</welcome-file>
  </welcome-file-list>
  <error-page>
    <error-code>500</error-code>
    <location>/faces/exception.xhtml</location>
  </error-page>
  <error-page>
    <exception-type>Exception</exception-type>
    <location>/faces/exception.xhtml</location>
  </error-page>

</web-app>

توجه کنید که در خط ۲۶، صفحه [index.xhtml] صفحه اصلی برنامه است.

[faces-config.xml]: برنامه JSF را پیکربندی می‌کند


<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.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-facesconfig_2_0.xsd">

  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
      <message-bundle>messages</message-bundle>
    <default-render-kit-id>PRIMEFACES_MOBILE</default-render-kit-id>
  </application>
</faces-config>

[beans.xml]: خالی اما برای تفسیر @Named الزامی است


<?xml version="1.0" encoding="UTF-8"?>
<beans 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/beans_1_0.xsd">
</beans>

[messages_fr.properties]: فایل پیام فرانسوی


#صفحه
page.titre=Les M\u00e9decins Associ\u00e9s
format.date=dd/MM/yyyy
format.date_detail=dd/MM/yyyy

#view1
vue1.header=Les M\u00e9decins Associ\u00e9s - R\u00e9servations

#فرم ۱
form1.titre=R\u00e9servations
form1.medecin=M\u00e9decin
form1.jour=Jour (jj/mm/aaaa)
form1.date.requise=La date est n\u00e9cessaire
form1.date.invalide=La date est invalide
form1.date.invalide_detail=La date est invalide
form1.agenda=Agenda
form1.options=Options

# فرم ۲
form2.titre={0} {1} {2}<br/>{3}
form2.titre_detail={0} {1} {2}<br/>{3}
form2.retour=Retour
form2.supprimer=Supprimer
form2.reserver=R\u00e9server
form2.precedent=Jour pr\u00e9c\u00e9dent
form2.suivant=Jour suivant
form2.today=Aujourd'hui

# فرم ۳
form3.client=Client
form3.valider=Valider
form3.titre={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,number,#00}-{6,number,#00}h:{7,number,#00}
form3.titre_detail={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,number,#00}-{6,number,#00}h:{7,number,#00}
form3.retour=Retour

#خطا
erreur.titre=Une erreur s'est produite.

#پیکربندی
config.retour=Retour
config.titre=Configuration
config.langue=Langue
config.langue.francais=Fran\u00e7ais
config.langue.anglais=Anglais
config.valider=Valider

#استثنا
exception.titre=Application indisponible. Veuillez recommencer ult\u00e9rieurement.

[messages_en.properties]: فایل پیام به زبان انگلیسی


# صفحه
page.titre=The Associated Doctors
format.date=dd/MM/yyyy
format.date_detail=dd/MM/yyyy

# view1
vue1.header=The Associated Doctors - Reservations

# فرم ۱
form1.titre=Reservations
form1.medecin=Doctor
form1.jour=day (dd/mm/yyyy)
form1.date.requise=The date is necessary
form1.date.invalide=Invalid date
form1.date.invalide_detail=invalid date
form1.agenda=Diary
form1.options=Options

# فرم ۲
form2.titre={0} {1} {2}<br/>{3}
form2.titre_detail={0} {1} {2}<br/>{3}
form2.retour=Back
form2.supprimer=Delete
form2.reserver=Reserve
form2.precedent=Previous Day
form2.suivant=Next Day
form2.today=Today

# فرم ۳
form3.client=Patient
form3.valider=Validate
form3.titre={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,number,#00}-{6,number,#00}h:{7,number,#00}
form3.titre_detail={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,number,#00}-{6,number,#00}h:{7,number,#00}
form3.retour=Back

# خطا
erreur.titre=Some error happened

#پیکربندی
config.retour=Back
config.titre=Configuration
config.langue=Language
config.langue.francais=French
config.langue.anglais=English
config.valider=Validate

#استثنا
exception.titre=Application not available. Please try again later.

9.6. صفحه [index.xhtml]

پروژه همیشه همان صفحه را نمایش می‌دهد، صفحه زیر: [index.xhtml]


<f:view xmlns="http://www.w3.org/1999/xhtml"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:p="http://primefaces.org/ui"
        xmlns:pm="http://primefaces.org/mobile"
        contentType="text/html"
        locale="#{form.locale}">

  <pm:page title="#{msg['page.titre']}">
    <pm:view id="vue1">
      <ui:fragment rendered="#{form.form1Rendered}">
        <ui:include src="vue1.xhtml"/>
      </ui:fragment>
      <ui:fragment rendered="#{form.erreurInit}">
        <ui:include src="vueErreurs.xhtml"/>
      </ui:fragment>
    </pm:view>
    <pm:view id="vue2">
      <ui:fragment rendered="#{form.form2Rendered}">
        <ui:include src="vue2.xhtml"/>
      </ui:fragment>
    </pm:view>
    <pm:view id="vue3">
      <ui:fragment rendered="#{form.form3Rendered}">
        <ui:include src="vue3.xhtml"/>
      </ui:fragment>
    </pm:view>
    <pm:view id="vueErreurs">
      <ui:fragment rendered="#{form.erreurRendered}">
        <ui:include src="vueErreurs.xhtml"/>
      </ui:fragment>
    </pm:view>
    <pm:view id="config">
      <ui:include src="config.xhtml"/>
    </pm:view>
  </pm:page>    
</f:view>
  • خط ۸: صفحه بین‌المللی‌سازی شده است (ویژگی locale
  • خط ۱۰: صفحه حاوی پنج نما است: view1 در خط ۱۱، view2 در خط ۱۹، view3 در خط ۲۴، vueErreurs در خط ۲۹، config در خط ۳۴. در هر لحظه، تنها یکی از این نماها قابل مشاهده است. هنگام شروع برنامه، view1 نمایش داده می‌شود. در اینجا با مشکل زیر مواجه شدیم: اگر برنامه با موفقیت راه‌اندازی شود، باید «[vue1.xhtml]» نمایش داده شود؛ در غیر این صورت، باید «[vueErreurs.xhtml]» نمایش داده شود. ما این مشکل را با اطمینان از اینکه محتوای view1 توسط مدل مدیریت می‌شود، حل کردیم؛ مدلی که مقادیر مربوط به بولین‌های [Form].form1rendered (خط ۱۲) و [Form] را تنظیم می‌کند.erreurInit (خط 15) برای تنظیم محتوای vue1 (خط 11)،

در یک شبیه‌ساز، نمای [vue1.xhtml] دارای رندر [1] است، نمای [vue2.xhtml] دارای رندر [2] است، و نما [vue3.xhtml] به صورت [3] رندر می‌شود:

نما [vueErreurs.xhtml]، تصویر رندر شده [4]؛ نما [config.xhtml]، تصویر رندر شده [5]:

9.7. لوبیاهای پروژه

کلاس در بسته [utils] قبلاً مورد بحث قرار گرفته است: کلاس [Messages] بین‌المللی‌سازی پیام‌های یک برنامه را تسهیل می‌کند. این کلاس در بخش 2.8.5.7 بررسی شد.

9.7.1. بیان Application

بیان [Application.java] یک بیان در مقیاس application است. همان‌طور که به یاد داریم، این نوع بیان برای ذخیره داده‌های فقط-خواندنی که برای همه کاربران برنامه در دسترس است، استفاده می‌شود. این بیان به شرح زیر است:


package beans;

import javax.ejb.EJB;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import rdvmedecins.metier.service.IMetierLocal;

@Named(value = "application")
@ApplicationScoped
public class Application {

  //لایه کسب‌وکار
  @EJB
  private IMetierLocal metier;

  public Application() {
  }
  
  // گیرنده‌ها

  public IMetierLocal getMetier() {
    return metier;
  }
  
}
  • خط ۸: به این بین نام 'application' داده شده است،
  • خط ۹: این bean در محدودهٔ application قرار دارد،
  • خطوط ۱۳–۱۴: یک مرجع به رابط محلی لایه [métier] توسط کانتینر EJB در سرور برنامه به آن تزریق خواهد شد. بیایید معماری برنامه را به یاد بیاوریم:

برنامه‌های PFM، EJB و [Metier] در همان JVM (ماشین مجازی جاوای) اجرا خواهند شد. بنابراین، لایه [PFM] از رابط محلی EJB استفاده خواهد کرد. همین. بیان [Application] هیچ چیز دیگری ندارد. برای دسترسی به لایه [métier]، سایر بیان‌ها آن را از این بیان بازیابی خواهند کرد.

9.7.2. بیان [Erreur]

کلاس [Erreur] به شرح زیر است:


package beans;

public class Erreur {
  
  public Erreur() {
  }
  
  // field
  private String classe;
  private String message;

  // سازنده
  public Erreur(String classe, String message){
    this.setClasse(classe);
    this.message=message;
  }
  
  // گیرنده و تنظیم‌کننده
...  
}
  • خط ۹: نام یک کلاس استثنا در صورتی که یک استثنا پرتاب شده باشد،
  • خط ۱۰: یک پیام خطا.

9.7.3. بیان [Form]

کد آن به شرح زیر است:


package beans;

...

@Named(value = "form")
@SessionScoped
public class Form implements Serializable {

  public Form() {
  }
  // بیان برنامه
  @Inject
  private Application application;
  private IMetierLocal metier;
  // کش جلسه
  private List<Medecin> medecins;
  private List<Client> clients;
  private Map<Long, Medecin> hMedecins = new HashMap<Long, Medecin>();
  private Map<Long, Client> hClients = new HashMap<Long, Client>();
  // مدل
  private Long idMedecin;
  private Date jour = new Date();
  private String strJour;
  private Boolean form1Rendered;
  private Boolean form2Rendered;
  private Boolean form3Rendered;
  private Boolean erreurRendered;
  private String form2Titre;
  private String form3Titre;
  private AgendaMedecinJour agendaMedecinJour;
  private Long idCreneauChoisi;
  private Medecin medecin;
  private Long idClient;
  private CreneauMedecinJour creneauChoisi;
  private List<Erreur> erreurs;
  private Boolean erreurInit = false;
  private String action;
  private String locale = "fr";
  private String msgErreurDate = "";
  private SimpleDateFormat dateFormatter;
  private Boolean erreurDate;
  
  @PostConstruct
  private void init() {
    System.out.println("init");
    // در ابتدا بدون خطا
    erreurInit = false;
    //قالب‌بندی تاریخ
    dateFormatter = new SimpleDateFormat(Messages.getMessage(null, "format.date", null).getSummary());
    dateFormatter.setLenient(false);
    // روز جاری
    strJour = dateFormatter.format(jour);
    // بازیابی لایه کسب‌وکار
    metier = application.getMetier();
    // کش کردن پزشکان و مشتریان
    try {
      medecins = metier.getAllMedecins();
      clients = metier.getAllClients();
    } catch (Throwable th) {
      //ثبت خطا
      erreurInit = true;
      prepareVueErreur(th);
      return;
    }
    //بررسی فهرست‌ها
    if (medecins.size() == 0) {
      // ثبت خطا
      erreurInit = true;
      erreurs = new ArrayList<Erreur>();
      erreurs.add(new Erreur("", "La liste des médecins est vide"));
    }
    if (clients.size() == 0) {
      //خطا ثبت شد
      erreurInit = true;
      erreurs = new ArrayList<Erreur>();
      erreurs.add(new Erreur("", "La liste des clients est vide"));
    }
    // خطا؟
    if (erreurInit) {
      //نمای خطا نمایش داده می‌شود
      setForms(false, false, false, true);
      return;
    }

    // فرهنگ‌ها
    for (Medecin m : medecins) {
      hMedecins.put(m.getId(), m);
    }
    for (Client c : clients) {
      hClients.put(c.getId(), c);
    }
    // نما ۱
    setForms(true, false, false, false);
  }

  // نمایش نما
  private void setForms(Boolean form1Rendered, Boolean form2Rendered, Boolean form3Rendered, Boolean erreurRendered) {
    this.form1Rendered = form1Rendered;
    this.form2Rendered = form2Rendered;
    this.form3Rendered = form3Rendered;
    this.erreurRendered = erreurRendered;
  }

  //آماده‌سازی vueErreur
  private void prepareVueErreur(Throwable th) {
    // فهرست خطاها ایجاد می‌شود
    erreurs = new ArrayList<Erreur>();
    erreurs.add(new Erreur(th.getClass().getName(), th.getMessage()));
    while (th.getCause() != null) {
      th = th.getCause();
      erreurs.add(new Erreur(th.getClass().getName(), th.getMessage()));
    }
//نمای خطاها نمایش داده می‌شود
    setForms(false, false, false, true);
  }

  // گیرنده و تنظیم‌کننده
  ..
}
  • خطوط ۵–۷: کلاس [Form] یک بین به نام «form» با دامنه جلسه (session scope) است. توجه داشته باشید که بنابراین این کلاس باید قابل سریالی‌سازی (serialisable) باشد،
  • خطوط ۱۲–۱۳: بین «form» مرجعی به بین «application» نگه می‌دارد. این مرجع توسط کانتینر servlet که برنامه در آن در حال اجرا است تزریق می‌شود (همانطور که با anotation @Inject نشان داده شده است).
  • خطوط ۲۴–۲۷: نمایش ویوهای vue1 (خط ۲۴)، vue2 (خط ۲۵)، vue3 (خط ۲۶) و vueErreurs (خط ۲۷) را کنترل می‌کنند،
  • خطوط ۴۳–۴۴: متد init بلافاصله پس از instantiate شدن کلاس اجرا می‌شود (که با annotation @PostConstruct نشان داده شده است)،
  • خطوط ۴۹–۵۰: فرمت تاریخ را مدیریت می‌کنند. PrimeFaces Mobile تقویمی ارائه نمی‌دهد. علاوه بر این، اعتبارسنج‌های JSF را نمی‌توان روی یک صفحه PFM استفاده کرد. بنابراین، ما باید ورود تاریخ تقویم را به صورت دستی مدیریت کنیم،
  • خط ۴۹: قالب تاریخ را تعیین می‌کند. این قالب از فایل‌های بین‌المللی‌شده گرفته شده است:

format.date=dd/MM/yyyy
format.date_detail=dd/MM/yyyy
  • خط ۵۰: این مشخص می‌کند که تاریخ نباید «laxiste» باشد. اگر این کار انجام نشود، ورودی‌ای مانند 32/12/2011 – که نادرست است – به‌عنوان تاریخ معتبر 01/01/2012 در نظر گرفته می‌شود،
  • خط ۵۴: یک مرجع از لایه [métier] از طریق bean [Application] بازیابی می‌شود،
  • خطوط ۵۷–۵۸: فهرست پزشکان و مشتریان از لایه [métier] درخواست می‌شود،
  • خطوط ۶۶–۹۱: اگر همه چیز به درستی پیش برود، فرهنگ‌های واژگان پزشکان و مشتریان ساخته می‌شوند. آنها بر اساس شماره خود فهرست‌بندی می‌شوند. سپس نما [vue1.xhtml] نمایش داده می‌شود (خط ۹۳)،
  • خط ۵۹: در صورت بروز خطا، قالب صفحه [vueErreurs.xhtml] تولید می‌شود. این قالب، لیست خطاها از خط ۳۵ است،
  • خطوط ۱۰۵–۱۱۵: متد [prepareVueErreur] لیست خطاهایی را که باید نمایش داده شوند، ایجاد می‌کند. سپس صفحه [index.xhtml] نما [vueErreurs.xhtml] را نمایش می‌دهد (خط ۱۱۴).

صفحه [erreur.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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- نمايش خطاها -->
  <pm:header title="#{msg['page.titre']}" swatch="b">
    <f:facet name="left">
      <p:button icon="home" value=" " href="#vue1?reverse=true" />
    </f:facet>
  </pm:header>
  <pm:content>
    <div align="center">
      <h1><h:outputText value="#{msg['erreur.titre']}" style="color: blue"/></h1>
    </div>

    <p:dataList value="#{form.erreurs}" var="erreur">
      <b>#{erreur.classe}</b> : <i>#{erreur.message}</i>
    </p:dataList>
  </pm:content>
</html>

این صفحه از تگ <p:dataList> (خطوط 21–23) برای نمایش لیست خطاها استفاده می‌کند. دکمه در خط 13 به شما امکان می‌دهد به view1 بازگردید.

  • دکمه [1] توسط خط ۱۳ تولید می‌شود. ویژگی icon آیکون دکمه را تعیین می‌کند. این دکمه به نمای vue1 بازمی‌گردد (ویژگی href
  • سربرگ [2] توسط خطوط 11–15 تولید می‌شود،
  • عنوان [3] توسط خط ۱۸ تولید می‌شود،
  • متن [4] توسط قالب #{erreur.classe} در خط ۲۲ تولید می‌شود،
  • متن [5] توسط قالب #{erreur.message} در خط 22 تولید می‌شود.

اکنون مراحل مختلف چرخهٔ عمر برنامه را تعریف می‌کنیم. برای هر اقدام کاربر، نماهای مرتبط و دستگیرکننده‌های رویدادی که در آن‌ها رخ می‌دهند را بررسی خواهیم کرد.

9.8. نمایش صفحهٔ اصلی

اگر همه چیز به درستی پیش برود، اولین نمایی که نمایش داده می‌شود [vue1.xhtml] است. این نمای زیر را تولید می‌کند:

کد مربوط به نمای [vue1.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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- نما 1 -->
  <pm:header title="#{msg['page.titre']}" swatch="b">
    <f:facet name="left">
      <p:button icon="gear" value=" "  href="#config" />
    </f:facet>
  </pm:header>
  <pm:content>
    <h:form id="form1">
      <div align="center">
        <h1><h:outputText value="#{msg['form1.titre']}" style="color: blue"/></h1>
      </div>
      <pm:field>
        <h:outputLabel value="#{msg['form1.medecin']}" for="choixMedecin"/>
        <h:selectOneMenu id="choixMedecin" value="#{form.idMedecin}">  
          <f:selectItems value="#{form.medecins}" var="medecin" itemLabel="#{medecin.titre} #{medecin.prenom} #{medecin.nom}" itemValue="#{medecin.id}"/>  
        </h:selectOneMenu>              
      </pm:field>
      <pm:field>
        <h:outputLabel value="#{msg['form1.jour']}" for="jour"/>
        <p:inputText id="jour" value="#{form.strJour}"/>
        <ui:fragment rendered="#{form.erreurDate}">
          <p:spacer width="50px"/>
          <h:outputText id="msgErreurDate" value="#{form.msgErreurDate}" style="color: red"/>
        </ui:fragment>
      </pm:field>
      <p:commandButton value="#{msg['form1.agenda']}" update=":form1, :vue2, :vueErreurs" action="#{form.getAgenda}" />
    </h:form>
  </pm:content>
</html>
  • خطوط ۱۱–۱۵: هدر [1] را تولید می‌کنند،
  • خط ۱۳: دکمه [2] را تولید می‌کند. کلیک بر روی آن نمای پیکربندی را نمایش می‌دهد (ویژگی href
  • خط ۱۹: عنوان [3] را تولید می‌کند،
  • خطوط ۲۱–۲۶: ایجاد لیست کشویی پزشکان [4]،
  • خطوط ۲۷–۳۴: فیلد ورودی تاریخ [5] را ایجاد می‌کنند. این فیلد ورودی با استفاده از تگ <p:inputText> بدون اعتبارسنج ایجاد شده است. تاریخ در سمت سرور اعتبارسنجی خواهد شد. اگر تاریخ نادرست باشد، سرور پیامی خطا را از طریق خطوط ۳۰–۳۴ نمایش خواهد داد،
  • خط ۳۵: دکمه‌ای که فرم را ارسال می‌کند. این دکمه سه فیلد را به‌روزرسانی می‌کند: form1 (فرم در view1view2 و vueErreurs. در واقع، اگر تاریخ نامعتبر باشد، باید form1 به‌روزرسانی شود. اگر تاریخ صحیح باشد، باید view2 به‌روزرسانی شود. در نهایت، اگر یک خطا رخ دهد (مانند قطع اتصال پایگاه داده)، باید vueErreurs نمایش داده شود. ممکن است وسوسه شوید که به جای form1 از vue1 (به‌روزرسانی کل نما) استفاده کنید. در این صورت، برنامه از کار می‌افتد.

این نما بر اساس مدل زیر است:


@Named(value = "form")
@SessionScoped
public class Form implements Serializable {

  public Form() {
  }

  // بیان برنامه
  @Inject
  private Application application;
  private IMetierLocal metier;
  // کش جلسه
  private List<Medecin> medecins;
  private List<Client> clients;
  // قالب
  private Long idMedecin;
  private Date jour = new Date();
  private String strJour;
  private Boolean form1Rendered;
  private Boolean form2Rendered;
  private Boolean form3Rendered;
  private Boolean erreurRendered;
  private String msgErreurDate = "";
  private Boolean erreurDate;
  
    // فهرست پزشکان
  public List<Medecin> getMedecins() {
    return medecins;
  }

  // تقویم
  public String getAgenda() {
...
    }
  }
  • میدان در خط 16 دسترسی خواندن و نوشتن به مقدار لیست در خط 23 صفحه را فراهم می‌کند. هنگامی که صفحه برای اولین بار نمایش داده می‌شود، مقدار انتخاب‌شده در کادر ترکیبی را تنظیم می‌کند؛
  • متد در خطوط 27–29 گزینه‌ها را برای لیست کشویی پزشکان (خط 24 نما) تولید می‌کند. هر گزینه تولید شده، برچسب (itemLabel) آن شامل عنوان، نام خانوادگی و نام پزشک خواهد بود و مقدار (itemValue) آن شامل شناسه پزشک خواهد بود،
  • میدان در خط ۱۸ دسترسی خواندن/نوشتن به میدان ورودی در خط ۲۹ صفحه را فراهم می‌کند،
  • خطوط ۳۲–۳۴: متد getAgenda کلیک روی دکمه [Agenda] در خط ۳۵ صفحه را مدیریت می‌کند. کد آن به شرح زیر است:

// تقویم
  public String getAgenda() {
    try {
      // بررسی روز
      jour = dateFormatter.parse(strJour);
      // بدون خطا
      erreurDate=false;
      msgErreurDate = "";
      // ایجاد تقویم
      return getAgenda(jour);
    } catch (ParseException ex) {
      // پیام خطا
      erreurDate=true;
      msgErreurDate = Messages.getMessage(null, "form1.date.invalide", null).getSummary();
      // view1      
      setForms(true, false, false, false);
      return "pm:vue1";
    }
  }
  • این متد با بررسی اعتبار تاریخی که کاربر وارد کرده است، آغاز می‌شود،
  • خط ۵: تاریخ parsée است، مطابق با فرمت تاریخ که توسط متد init هنگام نمونه‌سازی مدل مقداردهی اولیه شده است،
  • خط ۱۱: اگر یک استثنا رخ دهد، یک خطا تنظیم می‌شود (خط ۱۳)، یک پیام خطای بین‌المللی‌سازی‌شده ساخته می‌شود (خط ۱۴)، نمای vue1 آماده می‌شود (خط ۱۶) و نمای vue1 نمایش داده می‌شود (خط ۱۷)،
  • خط ۱۰: اگر تاریخ صحیح باشد، متد زیر اجرا می‌شود:

  // تقویم
  public String getAgenda(Date jour) {
    // هنوز هیچ شکافی انتخاب نشده است
    creneauChoisi = null;
    try {
      // بازیابی پزشک انتخاب‌شده
      medecin = hMedecins.get(idMedecin);
      // عنوان فرم ۲
      form2Titre = Messages.getMessage(null, "form2.titre", new Object[]{medecin.getTitre(), medecin.getPrenom(), medecin.getNom(), new SimpleDateFormat("dd MMM yyyy").format(jour)}).getSummary();
      //دفترچه یادداشت پزشک برای یک روز مشخص
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      // نمایش نما ۲
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      System.out.println(th);
      // نمایش خطا
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
}

این کد قبلاً چندین بار مشاهده شده است. خط ۹: یک پیام بین‌المللی‌سازی‌شده برای نمای vue2 ساخته می‌شود:


form2.titre={0} {1} {2}<br/>{3}
form2.titre_detail={0} {1} {2}<br/>{3}

توجه کنید که ما XHTML را در پیام گنجانده‌ایم. این پیام به صورت زیر نمایش داده خواهد شد:

9.9. نمایش دفترچه یادداشت پزشک

دفترچه یادداشت پزشک توسط نما [vue2.xhtml] نمایش داده می‌شود:

کد برای مشاهده [vue2.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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- نما ۲ -->
  <pm:header title="#{msg['page.titre']}" swatch="b"/>
  <pm:content>
    <h:form id="form2">
      <div align="center">
        <pm:buttonGroup orientation="horizontal">
          <p:commandButton inline="true" icon="back" value=" " action="#{form.showVue1}" update=":vue1"/>
          <p:commandButton inline="true" icon="minus" value=" " action="#{form.getPreviousAgenda}" update=":form2"/>
          <p:commandButton inline="true" icon="home" value=" " action="#{form.getTodayAgenda}" update=":form2"/>
          <p:commandButton inline="true" icon="plus" value=" " action="#{form.getNextAgenda}" update=":form2"/>
        </pm:buttonGroup>
        <h3><h:outputText value="#{form.form2Titre}" style="color: blue" escape="false"/></h3>
      </div>

      <p:dataList id="creneaux" type="inset" value="#{form.agendaMedecinJour.creneauxMedecinJour}" var="creneauMedecinJour">
        <p:column>
          <div align="center">
            <h2>
              <h:outputFormat value="{0,number,#00}h:{1,number,#00} - {2,number,#00}h:{3,number,#00}">
                <f:param value="#{creneauMedecinJour.creneau.hdebut}" />
                <f:param value="#{creneauMedecinJour.creneau.mdebut}" />
                <f:param value="#{creneauMedecinJour.creneau.hfin}" />
                <f:param value="#{creneauMedecinJour.creneau.mfin}" />
              </h:outputFormat>
              <ui:fragment rendered="#{creneauMedecinJour.rv!=null}">
                <br/>
                <h:outputText value="#{creneauMedecinJour.rv.client.titre} #{creneauMedecinJour.rv.client.prenom} #{creneauMedecinJour.rv.client.nom}" style="color: blue"/>
              </ui:fragment>
            </h2>
          </div>
          <div align="center">
            <ui:fragment rendered="#{creneauMedecinJour.rv!=null}">
              <p:commandButton inline="true" action="#{form.action}" value="#{msg['form2.supprimer']}" icon="minus" update=":form2, :vue3, :vueErreurs">
                <f:setPropertyActionListener value="#{creneauMedecinJour.creneau.id}" target="#{form.idCreneauChoisi}"/>
              </p:commandButton>
            </ui:fragment>
            <ui:fragment rendered="#{creneauMedecinJour.rv==null}">
              <p:commandButton inline="true" action="#{form.action}" value="#{msg['form2.reserver']}" icon="plus" update=":form2, :vue3, :vueErreurs">
                <f:setPropertyActionListener value="#{creneauMedecinJour.creneau.id}" target="#{form.idCreneauChoisi}"/>
              </p:commandButton>
            </ui:fragment>
          </div>
        </p:column>
      </p:dataList>
    </h:form>
  </pm:content>
</html>
  • خط ۱۱: هدر [1] را ایجاد می‌کند،
  • خطوط ۱۵–۲۰: گروه دکمه [2] را تولید می‌کند،
  • خط ۲۱: عنوان [3] را تولید می‌کند. به مقدار ویژگی 'escape' توجه کنید. این همان چیزی است که به ما امکان می‌دهد کد XHTML را که در داخل form2Titre قرار داده‌ایم، تفسیر کنیم،
  • خط ۲۴: اسلات‌های زمانی با استفاده از یک dataList نمایش داده می‌شوند،
  • خطوط ۲۸–۳۳: برچسب بازه زمانی [4] را تولید می‌کند،
  • خطوط ۳۴–۳۷: در صورت وجود قرار ملاقات در بازه زمانی، یک قطعه کد نمایش داده می‌شود،
  • خط ۳۶: نام مشتری که قرار ملاقات را رزرو کرده است را نمایش می‌دهد،
  • خطوط ۴۱–۴۵: نمایش دکمه [Supprimer] در صورت وجود قرار ملاقات،
  • خطوط ۴۶–۵۰: نمایش دکمه [Réserver] در صورت عدم وجود قرار ملاقات.

این نما عمدتاً توسط مدل زیر پر می‌شود:


private AgendaMedecinJour agendaMedecinJour;

که در خط ۲۴ فیلد dataList را پر می‌کند. این فیلد توسط متد getAgenda هنگام تغییر از view1 به view2 ایجاد شده است.

9.10. حذف یک قرار ملاقات

حذف یک قرار ملاقات به این ترتیب انجام می‌شود:

نما تحت تأثیر این اقدام به شرح زیر است:


<ui:fragment rendered="#{creneauMedecinJour.rv!=null}">
              <p:commandButton inline="true" action="#{form.action}" value="#{msg['form2.supprimer']}" icon="minus" update=":form2, :vueErreurs">
                <f:setPropertyActionListener value="#{creneauMedecinJour.creneau.id}" target="#{form.idCreneauChoisi}"/>
              </p:commandButton>
            </ui:fragment>
  • خط ۲: دکمه [Supprimer] با متد [Form].action مرتبط است (ویژگی action
  • خط ۳: شناسه (ID) اسلات (slot) انتخاب‌شده فعلی به مدل [Form].idCreneauChoisi ارسال خواهد شد،
  • خط ۲: فراخوانی AJAX فیلدهای فرم2 (فرم view2) و نمای vueErreurs را به‌روزرسانی خواهد کرد. در واقع دو سناریوی ممکن وجود دارد: اگر همه چیز به خوبی پیش برود، نمای «view2» مجدداً نمایش داده خواهد شد؛ در غیر این صورت، نمای vueErreurs نمایش داده خواهد شد.

روش [action] به شرح زیر است:


  // اقدام روی RV
  public String action() {
    // جستجوی یک شکاف در تقویم
    int i = 0;
    Boolean trouvé = false;
    while (!trouvé && i < agendaMedecinJour.getCreneauxMedecinJour().length) {
      if (agendaMedecinJour.getCreneauxMedecinJour()[i].getCreneau().getId() == idCreneauChoisi) {
        trouvé = true;
      } else {
        i++;
      }
    }
    //آیا یکی پیدا شده است؟
    if (!trouvé) {
      // عجیب است – فرم2 دوباره نمایش داده می‌شود
      setForms(false, true, false, false);
      return "pm:vue2";
    } else {
      creneauChoisi = agendaMedecinJour.getCreneauxMedecinJour()[i];
    }
    // پیدا شد
    // بسته به اقدام مورد نظر
    if (creneauChoisi.getRv() == null) {
      return reserver();
    } else {
      return supprimer();
    }
  }

  //رزرو
  public String reserver() {
 ...
  }

  public String supprimer() {
    try {
      // حذف یک قرار ملاقات
      metier.supprimerRv(creneauChoisi.getRv());
      // تقویم به‌روزرسانی می‌شود
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      //نمایش فرم 2
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      // نمایش خطا
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
}
  • خطوط ۳–۱۲: ما به دنبال بازه زمانی‌ای می‌گردیم که شناسه آن را دریافت کرده‌ایم (خط ۷)،
  • اگر پیدا نشود – که غیرعادی است – view2 دوباره نمایش داده می‌شود (خطوط 16–17)،
  • خط ۱۹: اگر پیدا شود، شیء متناظر [CreneauMedecinJour] ذخیره می‌شود. این همان چیزی است که دسترسی به قرار ملاقاتی را که باید حذف شود، در اختیار ما قرار می‌دهد،
  • خط ۲۶: آن را حذف می‌کنیم،
  • خطوط ۳۵–۴۸: متد حذف، در صورتی که حذف با موفقیت انجام شود (خطوط ۴۲–۴۳)، نمای vue2 را بازمی‌گرداند یا در صورت وجود مشکل (خطوط ۴۶–۴۷)، نمای vueErreurs را بازمی‌گرداند.

9.11. رزرو وقت ملاقات

فرآیند رزرو قرار ملاقات این توالی را دنبال می‌کند:

سیستم از نمای 2 به نمای 3 منتقل می‌شود. کد مربوط به این اقدام به شرح زیر است:


<ui:fragment rendered="#{creneauMedecinJour.rv==null}">
              <p:commandButton inline="true" action="#{form.action}" value="#{msg['form2.reserver']}" icon="plus" update=":vue3, :vueErreurs">
                <f:setPropertyActionListener value="#{creneauMedecinJour.creneau.id}" target="#{form.idCreneauChoisi}"/>
              </p:commandButton>
            </ui:fragment>
  • خط ۲: دکمه [Réserver] با متد [Form].action (ویژگی action) مرتبط است که همانند دکمه [Supprimer] است. فراخوانی AJAX نماهای 'vue3' و 'vueErreurs' را بسته به اینکه آیا در حین پردازش خطاهایی رخ دهد یا خیر، به‌روزرسانی می‌کند.
  • خط ۳: مانند دکمه [Supprimerشناسه بازه زمانی به قالب ارسال می‌شود.

قالبی که این اقدام را مدیریت می‌کند به شرح زیر است:


// اقدام روی RV
  public String action() {
    ...
    // بسته به اقدام مورد نظر
    if (creneauChoisi.getRv() == null) {
      return reserver();
    } else {
      return supprimer();
    }
  }

  // رزرو
  public String reserver() {
    try {
      // عنوان فرم ۳
      form3Titre = Messages.getMessage(null, "form3.titre", new Object[]{medecin.getTitre(), medecin.getPrenom(), medecin.getNom(), new SimpleDateFormat("dd MMM yyyy").format(jour),
                creneauChoisi.getCreneau().getHdebut(), creneauChoisi.getCreneau().getMdebut(), creneauChoisi.getCreneau().getHfin(), creneauChoisi.getCreneau().getMfin()}).getSummary();
      //فرم ۳ نمایش داده می‌شود
      setForms(false, false, true, false);
      return "pm:vue3";
    } catch (Throwable th) {
      //نمای خطاها
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
  }
  • خطوط ۲–۱۰: متد action مرجع creneauChoisi شیء [CreneauMedecinJour] را که موضوع یک رزرو است، تعیین می‌کند، سپس متد reserver را فراخوانی می‌کند،
  • خط 16: یک پیام بین‌المللی‌سازی‌شده ساخته می‌شود. به شرح زیر است:
form3.titre={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,number,#00}-{6,number,#00}h:{7,number,#00}
form3.titre_detail={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,number,#00}-{6,number,#00}h:{7,number,#00}

این عنوان نمای vue3 خواهد بود. مانند نمای vue2، این عنوان شامل XML است. همچنین شامل پارامترهایی است که برای نمایش زمان‌های بازه زمانی قالب‌بندی شده‌اند،

  • خطوط ۱۹–۲۰: نمای vue3 نمایش داده می‌شود،
  • خطوط ۲۳–۲۴: اگر با مشکلی مواجه شده باشد، نمای vueErreurs نمایش داده می‌شود.

9.12. تأیید قرار ملاقات

تأیید قرار ملاقات شامل توالی زیر است:

در [1]، نمای vue3، و در [2]، نمای vue2، پس از افزودن یک قرار ملاقات.

کد مربوط به vue3 ([vue3.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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- نما ۳ -->
  <pm:header title="#{msg['page.titre']}" swatch="b"/>
  <pm:content>
    <h:form id="form3">
      <p:commandButton inline="true" value=" " icon="back" action="#{form.showVue2}" update=":vue2"/>
      <div align="center">
        <h3><h:outputText value="#{form.form3Titre}" style="color: blue" escape="false"/></h3>
      </div>
      <pm:field>
        <h:outputLabel value="#{msg['form3.client']}" for="choixClient"/>
        <h:selectOneMenu id="choixClient" value="#{form.idClient}">
          <f:selectItems value="#{form.clients}" var="client" itemLabel="#{client.titre} #{client.prenom} #{client.nom}" itemValue="#{client.id}"/>
        </h:selectOneMenu>
      </pm:field>
      <div align="center">
        <p:commandButton inline="true" value="#{msg['form3.valider']}" action="#{form.validerRv}" update=":vue2, :vueErreurs" icon="check"/>
      </div>
    </h:form>
  </pm:content>
</html>
  • خط ۱۶: عنوان نما [3] را تولید می‌کند. به مقدار ویژگی escape توجه کنید که به کاراکترهای XHTML در عنوان اجازه می‌دهد تا تفسیر شوند،
  • خطوط ۱۸–۲۳: لیست کشویی مشتری [4] را تولید می‌کنند،
  • خط ۲۵: دکمه [Valider] [5] را تولید می‌کند. متد [Form].validerRv با این دکمه مرتبط است:

// اعتبارسنجی Rv
  public String validerRv() {
    try {
      // بازیابی یک نمونه از اسلات انتخاب‌شده
      Creneau creneau = metier.getCreneauById(idCreneauChoisi);
      // افزودن قرار ملاقات
      metier.ajouterRv(jour, creneau, hClients.get(idClient));
      // به‌روزرسانی تقویم
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      // نمایش فرم 2
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      // نمایش خطا
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
  }

این کد قبلاً در نسخه 01 مشاهده شده است. ما صرفاً نمایش ویوها را ثبت می‌کنیم:

  • نما 'vue2' (خطوط 11–12) در صورتی که همه چیز به خوبی پیش رفته باشد،
  • در غیر این صورت، نمای vueErreurs (خطوط 15–16).

9.13. لغو قرار ملاقات

این با توالی زیر مطابقت دارد:

دکمه [1] در نما [vue3.xhtml] به شرح زیر است:


      <p:commandButton inline="true" value=" " icon="back" action="#{form.showVue2}" update=":vue2"/>

بنابراین متد [Form].showVue2 فراخوانی می‌شود. این متد صرفاً view2 را نمایش می‌دهد:


  public String showVue2() {
    // نما ۲
    setForms(false, true, false, false);
    return "pm:vue2?reverse=true";
}

9.14. پیمایش تقویم

در view2، دکمه‌ها امکان پیمایش تقویم را فراهم می‌کنند:

روز قبل:

روز بعد:

امروز:

اگرچه در اسکرین‌شات‌های بالا نشان داده نشده است، تقویم به‌روزرسانی شده و قرارهای ملاقات روز جدید انتخاب‌شده را نمایش می‌دهد.

برچسب‌های سه دکمه مرتبط در [vue2.xhtml] به شرح زیر است:


<pm:buttonGroup orientation="horizontal">
          <p:commandButton inline="true" icon="back" value=" " action="#{form.showVue1}" update=":vue1"/>
          <p:commandButton inline="true" icon="minus" value=" " action="#{form.getPreviousAgenda}" update=":form2"/>
          <p:commandButton inline="true" icon="home" value=" " action="#{form.getTodayAgenda}" update=":form2"/>
          <p:commandButton inline="true" icon="plus" value=" " action="#{form.getNextAgenda}" update=":form2"/>
        </pm:buttonGroup>

متدهای [Form].getPreviousAgenda، [Form].getNextAgenda, [Form].today در مثال 03 بررسی شدند.

9.15. تغییر زبان نمایش

زبان را می‌توان با استفاده از دکمه‌ای در صفحهٔ اصلی تغییر داد:

کد دکمه به شرح زیر است:


      <p:button icon="gear" value=" "  href="#config" />

بنابراین به نمای پیکربندی [2] سوئیچ می‌کند. نمای [config.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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- نما ۱ -->
  <pm:header title="#{msg['page.titre']}" swatch="b">
    <f:facet name="left">
      <p:button icon="back" value=" " href="#vue1?reverse=true" />
    </f:facet>
  </pm:header>
  <pm:content>
    <h:form id="frmConfig">
      <div align="center">
        <h3><h:outputText value="#{msg['config.titre']}" style="color: blue"/></h3>
      </div>
      <pm:field>
        <h:outputLabel value="#{msg['config.langue']}" for="langue"/>
        <h:selectOneRadio id="langue" value="#{form.locale}">
          <f:selectItem itemLabel="#{msg['config.langue.francais']}" itemValue="fr"/>
          <f:selectItem itemLabel="#{msg['config.langue.anglais']}" itemValue="en" />
        </h:selectOneRadio>
      </pm:field>
      <p:commandButton value="#{msg['config.valider']}" action="#{form.configurer}" update=":vue1"/>
    </h:form>
  </pm:content>
</html>
  • خط ۱۱: نمایش [3]،
  • خط ۱۳: دکمه [4] را نمایش می‌دهد. این دکمه به شما امکان بازگشت به view1 را می‌دهد،
  • خط ۱۷: فرم مربوط به نما،
  • خط ۱۹: عنوان نما [5] را نمایش می‌دهد،
  • خطوط ۲۱–۲۷: دکمه‌های رادیویی را نمایش می‌دهند. مقدار (itemValue) دکمه رادیویی انتخاب‌شده به قالب [Form].locale (ویژگی value در خط ۲۳) ارسال خواهد شد،
  • خط ۲۸: دکمه [Valider] را نمایش می‌دهد. فراخوانی AJAX نمای vue1 را به‌روزرسانی می‌کند (ویژگی update). متد فراخوانی‌شده [Form].configurer است:

public String configurer(){
    // پس از پیکربندی – نمایش مجدد view1
    redirect();
    return null;
  }
  
  private void redirect() {
    // کلاینت به سرولت هدایت می‌شود
    ExternalContext ctx = FacesContext.getCurrentInstance().getExternalContext();
    try {
      ctx.redirect(ctx.getRequestContextPath());
    } catch (IOException ex) {
      Logger.getLogger(Form.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

متد </span>**configure**<span style="color: #000000"> (خط 1) به سادگی مرورگر موبایل را به URL برنامه هدایت می‌کند. بنابراین صفحه [index.xhtml] بارگذاری خواهد شد:


<f:view xmlns="http://www.w3.org/1999/xhtml"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:p="http://primefaces.org/ui"
        xmlns:pm="http://primefaces.org/mobile"
        contentType="text/html"
        locale="#{form.locale}">

  <pm:page title="#{msg['page.titre']}">
    <pm:view id="vue1">
      ...
    </pm:view>
    ...
  </pm:page>    
</f:view>
  • خط ۸: ویو از زبانی که به تازگی تغییر کرده است (ویژگی locale) استفاده خواهد کرد و view1 (خط ۱۱) را نمایش خواهد داد.

9.16. Conclusion

بیایید معماری برنامه‌ای را که همین حالا ساخته‌ایم مرور کنیم:

تغییر به رابط کاربری موبایل مستلزم بازنویسی صفحات بود XHTML. مدل، از سوی دیگر، تغییر بسیار کمی داشته است. لایه‌های پایین‌تر [métier]، [DAO] و [JPA] کاملاً بدون تغییر باقی مانده‌اند.

9.17. تست‌های اکلیپس

همان‌طور که برای نسخه‌های قبلی اپلیکیشن نمونه انجام دادیم، نشان خواهیم داد چگونه این نسخه 05 را با استفاده از Eclipse تست کنیم. ابتدا، پروژه‌های Maven مربوط به مثال 05 ([1]) را در Eclipse وارد می‌کنیم:

  • [mv-rdvmedecins-ejb-dao-jpa]: لایه‌های [DAO] و [JPA
  • [mv-rdvmedecins-ejb-metier]: لایه [métier
  • [mv-rdvmedecins-pfmobile]: لایه [web] که توسط PrimeFaces Mobile پیاده‌سازی شده است،
  • [mv-rdvmedecins-pfmobile-app]: پروژهٔ والد پروژهٔ سازمانی [mv-rdvmedecins-pfmobile-app-ear]. هنگامی که پروژهٔ والد وارد می‌شود، پروژهٔ فرزند به طور خودکار وارد می‌شود،
  • در [2]، پروژه سازمانی [mv-rdvmedecins-pfmobile-app-ear] اجرا می‌شود،
  • در [3]، سرور GlassFish را انتخاب کنید،
  • در [4]، در زبانه [Servers]، برنامه مستقر شده است. این برنامه به طور خودکار اجرا نمی‌شود. شما باید از طریق یک مرورگر یا شبیه‌ساز موبایل به آن دسترسی پیدا کنید: