Skip to content

5. مقدمة إلى مكتبة المكونات PrimeFaces

5.1. مكانة Primefaces في تطبيق JSF

لنعد إلى بنية تطبيق JSF كما درسناها في بداية هذا المستند:

تم إنشاء الصفحات JSF باستخدام ثلاث مكتبات علامات:

1
2
3
4
<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">
  • السطر 2: علامات <h:x> من مساحة الاسم [http://java.sun.com/jsf/html] التي تتوافق مع علامات HTML،
  • السطر 3: علامات <f:y> من مساحة الاسم [http://java.sun.com/jsf/core] التي تتوافق مع علامات JSF،
  • السطر 4: علامات <ui:z> من مساحة الاسم [http://java.sun.com/jsf/facelets] التي تتوافق مع علامات facelets.

لبناء صفحات JSF، سنضيف مكتبة علامات رابعة، وهي علامات مكونات Primefaces.

1
2
3
<html xmlns="http://www.w3.org/1999/xhtml"
      ...
xmlns:p="http://primefaces.org/ui">
  • السطر 3: العلامات <p:z> في مساحة الاسم [http://primefaces.org/ui] تتوافق مع مكونات Primefaces.

هذا هو التعديل الوحيد الذي سيظهر. وبالتالي، يظهر في العروض. تظل أدوات إدارة الأحداث والنماذج كما كانت مع JSF. هذه نقطة مهمة يجب فهمها.

يتيح استخدام مكونات Primefaces إنشاء واجهات ويب أكثر سهولة في الاستخدام بفضل المكونات العديدة في هذه المكتبة، وأكثر سلاسة بفضل تقنية AJAX التي تستخدمها بشكل أصلي. ونطلق على ذلك اسم الواجهات الغنية أو RIA (تطبيقات الإنترنت الغنية).

ستصبح بنية JSF السابقة هي بنية PF (Primefaces) التالية:

5.2. مزايا Primefaces

يقدم موقع Primefaces [http://www.primefaces.org/showcase/ui/home.jsf] قائمة بالمكونات التي يمكن استخدامها في صفحة PF:

في الأمثلة التالية، سنستخدم أول ميزتين من ميزات Primefaces:

  • بعض المكونات من بين المئات المتوفرة،
  • والسلوك الأصلي لهذه المكونات.

من بين المكونات المتوفرة:

لن نستخدم سوى حوالي خمسة عشر منها في أمثلةنا، ولكن هذا سيكون كافياً لفهم مبادئ إنشاء صفحة Primefaces.

5.3. تعلم Primefaces

يقدم Primefaces أمثلة على استخدام كل مكون من مكوناته. ما عليك سوى النقر على الرابط الخاص به. لنلقِ نظرة على أحد الأمثلة:

  • في [1]، المثال الخاص بالمكون [Spinner
  • في [2]، مربع الحوار الذي يظهر بعد النقر على الزر [Submit].

هناك ثلاث ميزات جديدة بالنسبة لنا:

  • المكون [Spinner] الذي لا يوجد أساسًا في JSF،
  • وكذلك الأمر بالنسبة لمربع الحوار،
  • وأخيرًا، فإن POST الناتج عن [Submit] يتم تنفيذه باستخدام Ajax. إذا لاحظنا المتصفح جيدًا أثناء POST، فلن نرى رمز الساعة الرملية. لا يتم إعادة تحميل الصفحة. بل يتم تعديلها ببساطة: يظهر مكون جديد، وهو في هذه الحالة مربع الحوار، في الصفحة.

لنرى كيف يتم إنتاج كل هذا. رمز XHTML في المثال هو التالي:


<h:form>
       <p:panel header="Spinners">
           <h:panelGrid id="grid" columns="2" cellpadding="5">
                <h:outputLabel for="spinnerBasic" value="Basic Spinner: " />
                <p:spinner id="spinnerBasic" value="#{spinnerController.number1}"/>
                <h:outputLabel for="spinnerStep" value="Step Factor: " />
                <p:spinner id="spinnerStep" value="#{spinnerController.number2}" stepFactor="0.25"/>
                <h:outputLabel for="minmax" value="Min/Max: " />
                <p:spinner id="minmax" value="#{spinnerController.number3}" min="0" max="100"/>
                <h:outputLabel for="prefix" value="Prefix: " />
                <p:spinner id="prefix" value="0" prefix="$" min="0" value="#{spinnerController.number4}"/>
           <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
           <p:outputPanel>
                   <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
                      <p:ajax update="ajaxspinnervalue" process="@this" />
               </p:spinner>
               <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
            </p:outputPanel>
           </h:panelGrid>
       </p:panel>
    <p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
    
    <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        ...
     </p:dialog>
</h:form>

أولاً، نلاحظ وجود علامات JSF التقليدية: <h:form> في السطر 1، <h:panelGrid> في السطر 3، <h:outputLabel> في السطر 4. يتم استرجاع بعض علامات JSF بواسطة PF وإثرائها: <p:commandButton> السطر 21. بعد ذلك، نجد علامات PF الخاصة بالتنسيق: <p:panel> السطر 2، <p:outputPanel> السطر 13، <p:dialog> السطر 23. وأخيرًا، لدينا علامات الإدخال: <p:spinner> السطر 5.

دعونا نحلل هذا الكود بالتوازي مع العرض:

  • في [1]، المكون الذي تم الحصول عليه باستخدام علامة <p:panel> في السطر 2،
  • في [2]، حقل الإدخال الذي تم الحصول عليه من خلال الجمع بين علامة <p:outputLabel> و<p:spinner>، السطران 6 و7،
  • إلى [3]، زر POST الذي تم الحصول عليه باستخدام العلامة <p:commandButton> في السطر 21،
  • في [4]، مربع الحوار في الأسطر 23-25،
  • في [5]، حاوية غير مرئية لمكونين. يتم إنشاؤها بواسطة العلامة <p:outputPanel> في السطر 13.

دعونا نحلل الكود التالي الذي ينفذ إجراء AJAX:


           <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
           <p:outputPanel>
                   <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
                      <p:ajax update="ajaxspinnervalue" process="@this" />
               </p:spinner>
               <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
</p:outputPanel>

يولد هذا الكود العرض التالي:

  • السطر 1: يعرض النص [1]. وهو في الوقت نفسه تسمية لمكون id=ajaxspinner (السمة for). هذا المكون هو المكون الموجود في السطر 3 (السمة id
  • الأسطر 3-5: تعرض المكون [2]. هذا المكون هو مكون إدخال/عرض مرتبط بالنموذج #{spinnerController.number5} (السمة value
  • السطر 6: يعرض المكون [3]. هذا المكون هو مكون عرض مرتبط بالنموذج #{spinnerController.number5} (السمة value
  • السطر 4: تضيف العلامة <p:ajax> سلوك AJAX إلى spinner. في كل مرة يتغير فيها هذا الأخير، يتم إنشاء POST بهذه القيمة (السمة process="@this") في النموذج #{spinnerController.number5}. وبمجرد القيام بذلك، يتم تحديث الصفحة (السمة update). قيمة هذه السمة هي معرف أحد مكونات الصفحة، وهو هنا المكون الموجود في السطر 6. ثم يتم تحديث المكون المستهدف للسمة update باستخدام النموذج. وهذا النموذج هو مرة أخرى #{spinnerController.number5}، أي قيمة spinner. وبالتالي، فإن الحقل [3] يتبع الإدخالات في الحقل [2].

لدينا هنا سلوك AJAX، وهو اختصار يعني Asynchronous Javascript And XML. وبشكل عام، فإن سلوك AJAX هو كما يلي:

  • يعرض المتصفح صفحة HTML تحتوي على كود جافا سكريبت (J من AJAX). تشكل عناصر الصفحة كائن جافا سكريبت يُسمى DOM (نموذج كائن المستند)،
  • يستضيف الخادم تطبيق الويب الذي أنتج هذه الصفحة،
  • في [1]، يحدث حدث في الصفحة. على سبيل المثال زيادة spinner. يتم إدارة هذا الحدث بواسطة جافا سكريبت،
  • في [2]، يقوم جافا سكريبت بإجراء POST على تطبيق الويب. ويقوم بذلك بشكل غير متزامن (الحرف A في AJAX). يمكن للمستخدم الاستمرار في العمل مع الصفحة. فهي ليست متجمدة، ولكن يمكن تجميدها إذا لزم الأمر. يقوم POST بتحديث قالب الصفحة استنادًا إلى القيم المرسلة، وهنا القالب #{spinnerController.number5}،
  • إلى [3]، وتقوم تطبيق الويب بإرجاع استجابة XML (حرف X من AJAX) أو JSON (JavaScript Object Notation
  • في [4]، يستخدم جافا سكريبت هذه الاستجابة لتحديث منطقة محددة من DOM، وهنا منطقة id=ajaxspinnervalue.

عند استخدام JSF و Primefaces، يتم إنشاء جافا سكريبت بواسطة Primefaces. تعتمد هذه المكتبة على مكتبة جافا سكريبت JQuery. وبالمثل، تعتمد مكونات Primefaces على مكونات مكتبة المكونات JQuery UI (واجهة المستخدم). لذا فإن JQuery هي أساس Primefaces.

لنعد إلى مثالنا ونعرض الآن POST الخاص بالزر [Submit]:

الرمز المرتبط بـ POST هو التالي:


<p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
    
    <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        <h:panelGrid id="display" columns="2" cellpadding="5">
            <h:outputText value="Value 1: " />
            <h:outputText value="#{spinnerController.number1}" /> 
            
            <h:outputText value="Value 2: " />
            <h:outputText value="#{spinnerController.number2}" /> 
            
            <h:outputText value="Value 3: " />
            <h:outputText value="#{spinnerController.number3}" /> 
            
            <h:outputText value="Value 4: " />
            <h:outputText value="#{spinnerController.number4}" /> 
            
            <h:outputText value="Value 5: " />
            <h:outputText value="#{spinnerController.number5}" /> 
        </h:panelGrid>
     </p:dialog>
                
</h:form>
  • السطر 1: يتم تشغيل POST بواسطة الزر الموجود في السطر 1. في Primefaces، تقوم العلامات التي تشغل POST بذلك افتراضيًا في شكل استدعاء AJAX. ولهذا السبب تحتوي هذه العلامات على سمة update للإشارة إلى المنطقة المراد تحديثها، بمجرد استلام الرد من الخادم. هنا، المنطقة التي يتم تحديثها هي panelGrid في السطر 4. لذا عند عودة POST، سيتم تحديث هذه المنطقة بالقيم التي تم إرسالها إلى النموذج. ومع ذلك، فهي موجودة داخل مربع حوار غير مرئي افتراضيًا. ويقوم السمة oncomplete في السطر 1 بعرضه. يحدث هذا الحدث عند انتهاء معالجة POST. قيمة هذا السمة هي كود جافا سكريبت. هنا، يتم عرض مربع الحوار الذي يحمل المعرف id=dialog، أي مربع الحوار الموجود في السطر 3 (السمة widgetVar
  • السطر 3: نرى سمات مختلفة لمربع الحوار. يجب التجربة لمعرفة وظائفها.

لقد تحدثنا عن النموذج ولكننا لم نعرضه بعد. إنه هذا:

public class SpinnerController {

    private int number1;
    private double number2;
    private int number3;
    private int number4;
    private int number5;

     // الوصول والضبط
...
}

بشكل عام، يمكننا اتباع الخطوات التالية:

  • تحديد مكون Primefaces الذي نريد استخدامه،
  • ودراسة مثاله. أمثلة Primefaces جيدة الصنع وسهلة الفهم.

5.4. مشروع Primefaces الأول: mv-pf-01

لنقم بإنشاء مشروع ويب Maven باستخدام Netbeans:

  • [1, 2, 3]: نقوم بإنشاء مشروع Maven من النوع [Web Application
  • [4]: سيكون الخادم هو Tomcat،
  • في [5]، المشروع الذي تم إنشاؤه،
  • في [6]، نقوم بتنظيفه من الملف [index.jsp] وحزمة Java،
  • في [7, 8]: في خصائص المشروع، نضيف دعمًا لـ Java Server Faces،
  • إلى [9]، وفي علامة التبويب [Components] نختار مكتبة مكونات Primefaces. يوفر Netbeans دعمًا لمكتبات مكونات أخرى: ICEFaces و RichFaces.
  • في [10]، المشروع الذي تم إنشاؤه. في [11]، لاحظ التبعية على Primefaces.

ببساطة، مشروع Primefaces هو مشروع JSF كلاسيكي تمت إضافة تبعية لـ Primefaces إليه. لا أكثر.

بعد فهم ذلك، نقوم بتعديل الملف [pom.xml] للعمل مع أحدث إصدارات المكتبات:


    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.1.8</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>3.3</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <repositories>
    <repository>
      <id>jsf20</id>
      <name>Repository for library Library[jsf20]</name>
      <url>http://download.java.net/maven/2/</url>
    </repository>
    <repository>
      <id>primefaces</id>
      <name>Repository for library Library[primefaces]</name>
      <url>http://repository.primefaces.org/</url>
    </repository>
</repositories>

في الأسطر 26-30، لاحظ مستودع Maven لـ Primefaces. بعد إجراء هذه التعديلات، نقوم ببناء المشروع لبدء تنزيل التبعيات. نحصل عندئذٍ على المشروع [12].

الآن، دعونا نحاول إعادة إنتاج المثال الذي درسناه. تصبح الصفحة [index.html] كما يلي:


<?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:ui="http://java.sun.com/jsf/facelets">
  <h:head>
    <title>Spinner</title>
  </h:head>
  <h:body>
    <!-- نموذج -->
    <h:form>
      <p:panel header="Spinners">
        <h:panelGrid id="grid" columns="2" cellpadding="5">
          <h:outputLabel for="spinnerBasic" value="Basic Spinner: " />
          <p:spinner id="spinnerBasic" value="#{spinnerController.number1}"/>
          <h:outputLabel for="spinnerStep" value="Step Factor: " />
          <p:spinner id="spinnerStep" value="#{spinnerController.number2}" stepFactor="0.25"/>
          <h:outputLabel for="minmax" value="Min/Max: " />
          <p:spinner id="minmax" value="#{spinnerController.number3}" min="0" max="100"/>
          <h:outputLabel for="prefix" value="Prefix: " />
          <p:spinner id="prefix" prefix="$" min="0" value="#{spinnerController.number4}"/>
          <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
          <p:outputPanel>
            <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
              <p:ajax update="ajaxspinnervalue" process="@this" />
            </p:spinner>
            <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
          </p:outputPanel>
        </h:panelGrid>
      </p:panel>
      <p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
      <!-- مربع حوار -->
      <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        <h:panelGrid id="display" columns="2" cellpadding="5">
          <h:outputText value="Value 1: " />
          <h:outputText value="#{spinnerController.number1}" /> 
          <h:outputText value="Value 2: " />
          <h:outputText value="#{spinnerController.number2}" /> 
          <h:outputText value="Value 3: " />
          <h:outputText value="#{spinnerController.number3}" /> 
          <h:outputText value="Value 4: " />
          <h:outputText value="#{spinnerController.number4}" /> 
          <h:outputText value="Value 5: " />
          <h:outputText value="#{spinnerController.number5}" /> 
        </h:panelGrid>
      </p:dialog>
    </h:form>
  </h:body>
</html>

لا ننسى السطر 5 الذي يعلن مساحة اسم مكتبة علامات Primefaces. نضيف إلى المشروع، الحبة التي تستخدم كنموذج للصفحة:

  

البيان هو التالي:


package beans;

import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean
@RequestScoped
public class SpinnerController {

  // نموذج
  private int number1;
  private double number2;
  private int number3;
  private int number4;
  private int number5;

  // مُستقبلات ومُعيّنات
  ...
}

الفئة هي bean (السطر 6) ذات نطاق الاستعلام (السطر 7). وبما أننا لم نحدد اسمًا، فإن bean يحمل اسم الفئة مع الحرف الأول صغيرًا: spinnerController.

عند تشغيل المشروع، نحصل على ما يلي:

 

لقد أوضحنا للتو كيفية اختبار مثال مأخوذ من موقع Primefaces. ويمكن اختبار جميع الأمثلة بهذه الطريقة.

في ما يلي، سنركز فقط على بعض مكونات Primefaces. سنبدأ أولاً باستعراض الأمثلة التي درسناها باستخدام JSF واستبدال بعض علامات JSF بعلامات Primefaces. سيتغير مظهر الصفحات قليلاً، وستعمل بطريقة AJAX، لكن لن يكون هناك حاجة لتغيير الفاصوليا المرتبطة بها. في كل من الأمثلة التالية، سنكتفي بعرض كود الصفحات ونسخ الشاشة المرتبطة بها. ندعو القارئ إلى اختبار الأمثلة لاكتشاف الاختلافات بين صفحات JSF وصفحات PF.

5.5. المثال mv-pf-02: مدير الأحداث – التدويل – التنقل بين الصفحات

هذا المشروع هو نسخة معدلة من المشروع JSF [mv-jsf2-02] (الفقرة 2.4، الصفحة 41):

مشروع Netbeans هو التالي:

الصفحة [index.html] هي التالية:


<?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:ui="http://java.sun.com/jsf/facelets">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </h:head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <p:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}" ajax="false"/>
          <p:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}" ajax="false"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <p:commandLink value="#{msg['welcome.page1']}" action="page1" ajax="false"/>
      </h:form>
    </body>
  </f:view>
</html>

في الأسطر 15 و16 و19، تم استبدال العلامات <h:commandLink> بعلامات <p:commandLink>. تتصرف هذه العلامة بشكل افتراضي كـ AJAX، ويمكن تعطيلها عن طريق تعيين السمة ajax="false". لذلك، هنا، تعمل علامات <p:commandLink> كعلامات <h:commandLink>: سيتم إعادة تحميل الصفحة عند النقر على هذه الروابط.

5.6. مثال mv-pf-03: تخطيط الصفحة باستخدام facelets

يعرض هذا المشروع إنشاء صفحات XHTML باستخدام نماذج facelets من المثال [mv-jsf2-09] (الفقرة 2.11):

 

مشروع Netbeans هو التالي:

  • في [1]، ملفات تكوين المشروع JSF،
  • في [2]، الصفحات XHTML،
  • إلى [3]، وحبة الدعم لتغيير اللغات،
  • في [4]، ملفات الرسائل،
  • إلى [5]، التبعيات.

تستند صفحات المشروع إلى نموذج الصفحة [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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
      <h:form id="formulaire">
        <table style="width: 600px">
          <tr>
            <td colspan="2" bgcolor="#ccccff">
              <ui:include src="entete.xhtml"/>
            </td>
          </tr>
          <tr>
            <td style="width: 100px; height: 200px" bgcolor="#ffcccc">
              <ui:include src="menu.xhtml"/>
            </td>
            <td>
              <p:outputPanel id="contenu">
                <ui:insert name="contenu" >
                  <h2>Contenu</h2>
                </ui:insert>
              </p:outputPanel>
            </td>
          </tr>
          <tr bgcolor="#ffcc66">
            <td colspan="2">
              <ui:include src="basdepage.xhtml"/>
            </td>
          </tr>         
        </table>
      </h:form>
    </h:body>
  </f:view>
</html>
  • السطر 9: علامة <f:view> تحيط بالصفحة بأكملها للاستفادة من الترجمة التي تتيحها،
  • السطر 15: نموذج معرف النموذج. يشكل هذا النموذج نص الصفحة. في هذا النص، لا يوجد سوى جزء ديناميكي واحد، وهو الأسطر 28-30. وهنا سيتم إدراج الجزء المتغير من الصفحة:
  • سيتم تحديث المنطقة المحددة أعلاه بواسطة استدعاءات AJAX. من أجل تحديدها، قمنا بتضمينها في حاوية Primefaces تم إنشاؤها بواسطة العلامة <p:outputPanel> (السطر 27). وقد تم تسمية هذا الحاوية بـ "محتوى" (السمة id). ونظرًا لوجودها في نموذج يمثل بدوره حاوية باسم "نموذج"، فإن الاسم الكامل للمنطقة الديناميكية هو:formulaire:contenu. يشير الرمز : الأول إلى أننا نبدأ من جذر المستند، ثم ننتقل إلى الحاوية المسماة "نموذج"، ثم إلى الحاوية المسماة "محتوى". تكمن إحدى الصعوبات في AJAX في تسمية المناطق التي يجب تحديثها بشكل صحيح من خلال استدعاء AJAX. وأبسط طريقة هي الاطلاع على كود المصدر للصفحة HTML المستلمة:

1
2
3
            <td><span id="formulaire:contenu">
                  <h2>Contenu</h2></span>
</td>

فيما سبق، نرى أن العلامة <h:outputPanel> قد أنشأت علامة HTML <span>. في هذا المثال، يشير الاسم النسبي formulaire:contenu (بدون علامة : في البداية) والاسم الكامل :formulaire:contenu (مع علامة : في البداية) إلى نفس الكائن.

يجب ملاحظة أن استدعاءات AJAX (<p:commandButton>، <p:commandLink>) التي تقوم بتحديث المنطقة الديناميكية ستحتوي على السمة update=":formulaire:contenu".

الصفحة [index.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:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:fragment rendered="#{requestScope.page1 || requestScope.page2==null}">
        <ui:include src="page1.xhtml"/>
      </ui:fragment>
      <ui:fragment rendered="#{requestScope.page2}">
        <ui:include src="page2.xhtml"/>
      </ui:fragment>
    </ui:define>
  </ui:composition>
</html>
  • السطر 8، نموذج [index.xhtml] هو الصفحة [layout.xhtml] التي تم عرضها للتو،
  • السطر 9، هي منطقة معرف المحتوى التي يتم تحديثها بواسطة [index.html]. في هذه المنطقة، يوجد جزءان:
    • الجزء [page1.xhtml] السطر 11؛
    • الجزء [page2.xhtml] في السطر 14.

هذان الجزءان متعارضان.

  • السطر 10: يتم عرض المقطع [page1.xhtml] إذا كان السمة page1 في الطلب مضبوطة على true أو إذا كانت السمة page2 غير موجودة. وهذا هو الحال في الطلب الأول تمامًا حيث لن يتواجد أي من هذين السمتين في الطلب. في هذه الحالة، سيتم عرض المقطع [page1.xhtml
  • السطر 11، يتم عرض المقطع [page2.xhtml] إذا كان للطلب السمة page2 بقيمة true

المقتطف [page1.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:ui="http://java.sun.com/jsf/facelets">

  <body>
    <h:panelGrid columns="2">
      <p:commandLink value="#{msg['page1.langue1']}" actionListener="#{changeLocale.setFrenchLocale}" ajax="true" update=":formulaire:contenu"/>
      <p:commandLink value="#{msg['page1.langue2']}" actionListener="#{changeLocale.setEnglishLocale}" ajax="true" update=":formulaire:contenu"/>
    </h:panelGrid>
    <h1><h:outputText value="#{msg['page1.titre']}" /></h1>
     <p:commandLink value="#{msg['page1.lien']}" update=":formulaire:contenu">
      <f:setPropertyActionListener value="#{true}" target="#{requestScope.page2}" />  
    </p:commandLink>
  </body>
</html>

ويعرض المحتوى التالي:

  • السطران 11 و 12، الرابطان لتغيير اللغة. يؤدي هذان الرابطان إلى استدعاءات AJAX (ajax=true). هذه هي القيمة الافتراضية. لذلك يمكننا عدم وضع السمة ajax=true. ولن نقوم بذلك بعد الآن. تجدر الإشارة إلى أن هذين الرابطين يقومان بتحديث منطقة :form:content (السمة update)، وهي المنطقة المحددة أعلاه،
  • السطر 15: رابط تنقل AJAX يقوم هو الآخر بتحديث المنطقة :formulaire:contenu،
  • السطر 16: نستخدم العلامة <h:setPropertyActionListener> لوضع السمة page2 في الاستعلام بالقيمة true. سيؤدي ذلك إلى عرض الجزء [page2.xhtml] (السطر 6 أدناه) في الصفحة [index.xhtml]:

  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:fragment rendered="#{requestScope.page1 || requestScope.page2==null}">
        <ui:include src="page1.xhtml"/>
      </ui:fragment>
      <ui:fragment rendered="#{requestScope.page2}">
        <ui:include src="page2.xhtml"/>
      </ui:fragment>
    </ui:define>
</ui:composition>

المقتطف [page2.xhtml] مشابه:

رمز [page2.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:ui="http://java.sun.com/jsf/facelets">

  <body>
    <h1><h:outputText value="#{msg['page2.entete']}"/></h1>
    <p:commandLink value="#{msg['page2.lien']}" update=":formulaire:contenu">
      <f:setPropertyActionListener value="#{true}" target="#{requestScope.page1}" />  
    </p:commandLink>
  </body>
</html>

من هذا المثال، سنستخلص النقاط التالية لاستخدامها لاحقًا:

  • سنستخدم النموذج [layout.xhtml] كنموذج للصفحات،
  • سيتم تحديد المنطقة الديناميكية بواسطة المعرف:formulaire:contenu وسيتم تحديثها عن طريق استدعاءات AJAX.

5.7. مثال mv-pf-04: نموذج الإدخال

هذا المشروع هو نسخة معدلة من المشروع JSF2 [mv-jsf2-03] (انظر الفقرة 2.5):

مشروع Netbeans هو التالي:

أعلاه، في [1]، صفحات XHTML للمشروع. يتم تخطيط الصفحة بواسطة النموذج [layout.xhtml] الذي تمت دراسته سابقًا. الصفحة [index.xhtml] هي الصفحة الوحيدة في المشروع. يتم عرضها في منطقة:formulaire:contenu. رمزها هو التالي:


<?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:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:include src="page1.xhtml"/>
    </ui:define>
  </ui:composition>
</html>

وهي تكتفي بعرض الجزء [page1.xhtml]. وهذا الجزء هو ما يعادل النموذج الذي تمت دراسته في المثال [mv-jsf2-03]. تجدر الإشارة إلى أن الغرض من هذا النموذج كان عرض علامات الإدخال JSF. وقد تم استبدال هذه العلامات هنا بعلامات Primefaces.

PanelGrid

لتنسيق عناصر [page1.xhtml]، نستخدم العلامة <p:panelGrid>. على سبيل المثال، بالنسبة لرابطي اللغتين:


<!-- اللغات -->
    <p:panelGrid columns="2">
      <p:commandLink value="#{msg['form.langue1']}" actionListener="#{changeLocale.setFrenchLocale}" update=":formulaire:contenu"/>
      <p:commandLink value="#{msg['form.langue2']}" actionListener="#{changeLocale.setEnglishLocale}" update=":formulaire:contenu"/>
    </p:panelGrid>

ينتج عن ذلك العرض التالي:

 

هناك شكل آخر لعلامة <p:panelGrid> وهو كما يلي:


<p:panelGrid>

      <f:facet name="header">  
        <p:row>  
          <p:column colspan="3"><h:outputText value="#{msg['form.titre']}"/></p:column>  
        </p:row>  
        <p:row>  
          <p:column><h:outputText value="#{msg['form.headerCol1']}"/></p:column>  
          <p:column><h:outputText value="#{msg['form.headerCol2']}"/></p:column>  
          <p:column><h:outputText value="#{msg['form.headerCol3']}"/></p:column>  
        </p:row>  
      </f:facet>        

      <p:row>
        <p:column>
          <h:outputText value="inputText"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputText" value="#{msg['form.loginPrompt']}" />  
          <p:inputText id="inputText" value="#{form.inputText}"/>
        </p:column>
        <p:column>
          <h:outputText id="inputTextValue" value="#{form.inputText}"/>
        </p:column>
      </p:row>
...
     <f:facet name="footer">
        <p:row>
          <p:column colspan="3">
            <div align="center">
              <p:commandButton value="#{msg['form.submitText']}" update=":formulaire:contenu"/>
            </div>
          </p:column>
        </p:row>
      </f:facet>    
</p:panelGrid>

يتم تحديد صفوف وأعمدة الجدول بواسطة العلامات <p:row> و <p:column>.

تحدد الأسطر 3-12 رأس الجدول:

 

تحدد الأسطر 14-25 سطرًا من الجدول:

 

تحدد الأسطر 27-35 تذييل الجدول:

 

inputText


      <p:row>
        <p:column>
          <h:outputText value="inputText"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputText" value="#{msg['form.loginPrompt']}" />  
          <p:inputText id="inputText" value="#{form.inputText}"/>
        </p:column>
        <p:column>
          <h:outputText id="inputTextValue" value="#{form.inputText}"/>
        </p:column>
</p:row>
 

password


<p:row>
        <p:column>
          <h:outputText value="inputSecret"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputSecret" value="#{msg['form.passwdPrompt']}"/>
          <p:password id="inputSecret" value="#{form.inputSecret}" feedback="true"   
               promptLabel="#{msg['form.promptLabel']}" weakLabel="#{msg['form.weakLabel']}"  
               goodLabel="#{msg['form.goodLabel']}" strongLabel="#{msg['form.strongLabel']}" />  
        </p:column>
        <p:column>
          <h:outputText id="inputSecretValue" value="#{form.inputSecret}"/>
        </p:column>
      </p:row>

السطر 7، السمة feedback=true تسمح بالحصول على تقييم لجودة كلمة المرور.

inputTextArea


<p:row>
        <p:column>
          <h:outputText value="inputTextArea"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputTextArea" value="#{msg['form.descPrompt']}"/>
          <p:editor id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
        </p:column> 
        <p:column>
          <h:outputText id="inputTextAreaValue" value="#{form.inputTextArea}"/>
        </p:column>
      </p:row>

السطر 7، تعرض علامة <p:editor> محررًا متطورًا يتيح تنسيق النص (الخط، الحجم، اللون، المحاذاة، ...). ما يتم إرساله إلى الخادم هو رمز HTML للنص الذي تم إدخاله [2].

selectOneListBox


<p:row>
        <p:column>
          <h:outputText value="selectOneListBox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneListBox1" value="#{msg['form.selectOneListBox1Prompt']}"/>
          <p:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}">
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
          </p:selectOneListbox>
        </p:column>
        <p:column>
          <h:outputText id="selectOneListBox1Value" value="#{form.selectOneListBox1}"/>
        </p:column>
      </p:row>
 

selectOneMenu


<p:row>
        <p:column>
          <h:outputText value="selectOneMenu"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneMenu" value="#{msg['form.selectOneMenuPrompt']}"/>
          <p:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
            <f:selectItem itemValue="4" itemLabel="quatre"/>
            <f:selectItem itemValue="5" itemLabel="cinq"/>
          </p:selectOneMenu>
        </p:column>
        <p:column>
          <h:outputText id="selectOneMenuValue" value="#{form.selectOneMenu}"/>
        </p:column>
      </p:row>
 

selectManyMenu


<p:row>
        <p:column>
          <h:outputText value="selectManyMenu"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectManyMenu" value="#{msg['form.selectManyMenuPrompt']}"/>
          <p:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
            <f:selectItem itemValue="4" itemLabel="quatre"/>
            <f:selectItem itemValue="5" itemLabel="cinq"/>
          </p:selectManyMenu>
          <p:commandLink value="#{msg['form.buttonRazText']}" actionListener="#{form.clearSelectManyMenu()}" update=":formulaire:selectManyMenu" style="margin-left: 10px"/>
        </p:column>
        <p:column>
          <h:outputText id="selectManyMenuValue" value="#{form.selectManyMenuValue}"/>
        </p:column>
      </p:row>
 

في السطر 14، تجدر الإشارة إلى أن الرابط [Raz] يقوم بتحديث AJAX للمنطقة :formulaire:selectManyMenu التي تتوافق مع مكون السطر 6. ومع ذلك، يجب معرفة أنه أثناء POST AJAX، يتم إرسال جميع قيم النموذج. وبالتالي يتم تحديث النموذج بالكامل. ومع هذا النموذج، لا يتم تحديث سوى المنطقة :formulaire:selectManyMenu.

selectBooleanCheckbox


<p:row>
        <p:column>
          <h:outputText value="selectBooleanCheckbox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectBooleanCheckbox" value="#{msg['form.selectBooleanCheckboxPrompt']}"/>
          <p:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/>
        </p:column>
        <p:column>
          <h:outputText id="selectBooleanCheckboxValue" value="#{form.selectBooleanCheckbox}"/>
        </p:column>
      </p:row>
 

selectManyCheckbox


<p:row>
        <p:column>
          <h:outputText value="selectManyCheckbox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectManyCheckbox" value="#{msg['form.selectManyCheckboxPrompt']}"/>
          <p:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
            <f:selectItem itemValue="1" itemLabel="rouge"/>
            <f:selectItem itemValue="2" itemLabel="bleu"/>
            <f:selectItem itemValue="3" itemLabel="blanc"/>
            <f:selectItem itemValue="4" itemLabel="noir"/>
          </p:selectManyCheckbox>
        </p:column>
        <p:column>
          <h:outputText id="selectManyCheckboxValue" value="#{form.selectManyCheckboxValue}"/>
        </p:column>
      </p:row>
 

selectOneRadio


<p:row>
        <p:column>
          <h:outputText value="selectOneRadio"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneRadio" value="#{msg['form.selectOneRadioPrompt']}"/>
          <p:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}" >
            <f:selectItem itemValue="1" itemLabel="voiture"/>
            <f:selectItem itemValue="2" itemLabel="vélo"/>
            <f:selectItem itemValue="3" itemLabel="scooter"/>
            <f:selectItem itemValue="4" itemLabel="marche"/>
          </p:selectOneRadio>
        </p:column>
        <p:column>
          <h:outputText id="selectOneRadioValue" value="#{form.selectOneRadio}"/>
        </p:column>
      </p:row>
 

5.8. مثال: mv-pf-05: قوائم ديناميكية

هذا المشروع هو نسخة معدلة من المشروع JSF2 [mv-jsf2-04] (انظر الفقرة 2.6):

Image

لا يضيف هذا المشروع علامات Primefaces جديدة مقارنة بالمشروع السابق. ولذلك لن نعلق عليه. وهو جزء من قائمة الأمثلة المتاحة للقارئ على موقع الوثيقة.

5.9. مثال: mv-pf-06: التنقل – الجلسة – إدارة الاستثناءات

هذا المشروع هو نسخة معدلة من المشروع JSF2 [mv-jsf2-05] (انظر الفقرة 2.7):

مرة أخرى، لا يضيف هذا المثال علامات Primefaces جديدة. سنكتفي بالتعليق على جدول الروابط الموضح أعلاه:


<p:panelGrid columns="6">
  <p:commandLink value="1" action="form1?faces-redirect=true" ajax="false"/>
  <p:commandLink value="2" action="#{form.doAction2}" ajax="false"/>
  <p:commandLink value="3" action="form3?faces-redirect=true" ajax="false"/>
  <p:commandLink value="4" action="#{form.doAction4}" ajax="false"/>
  <p:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}" ajax="false"/>
  <p:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}" ajax="false"/>
</p:panelGrid>
  • تحتوي جميع الروابط على السمة ajax=false. وبالتالي، يتم تحميل الصفحة بشكل عادي،
  • ونلاحظ في السطرين 2 و4 طريقة إجراء إعادة التوجيه.

5.10. مثال: mv-pf-07: التحقق من صحة المدخلات وتحويلها

هذا المشروع هو نسخة معدلة من المشروع JSF2 [mv-jsf2-06] (انظر الفقرة 2.8):

Image

يقدم التطبيق علامتين جديدتين، علامة <p:messages>:


<p:messages globalOnly="true"/>

Image

وعلامة <p:message>:


<p:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<p:message for="saisie1" styleClass="error"/>

بالمقارنة مع العلامة <h:message> في JSF، فإن العلامة <p:message> في PF تجلب التغييرات التالية:

  • يختلف مظهر رسالة الخطأ [1]،
  • ومنطقة الإدخال الخاطئة محاطة بإطار أحمر في [2].

5.11. مثال: mv-pf-08: الأحداث المرتبطة بتغيير حالة المكونات

هذا المشروع هو نسخة معدلة من المشروع JSF2 [mv-jsf2-07] (انظر الفقرة 2.9):

Image

قدم المشروع JSF مفهوم listeners. تمت إدارة listener مع Primefaces بطريقة مختلفة.

مع JSF:

1
2
3
4
5
6
7
         <!-- السطر 1 -->
        <h:outputText value="#{msg['combo1.prompt']}"/>
        <h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
        </h:selectOneMenu>
        <h:panelGroup></h:panelGroup>
<h:outputText value="#{form.combo1}"/>

مع Primefaces:

<h:outputText value="#{msg['combo1.prompt']}"/>
        <p:selectOneMenu id="combo1" value="#{form.combo1}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
          <p:ajax update=":formulaire:combo2"/>  
        </p:selectOneMenu>
        <h:panelGroup></h:panelGroup>
        <h:outputText value="#{form.combo1}"/>

        <h:outputText value="#{msg['combo2.prompt']}"/>
        <p:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
          <f:selectItems value="#{form.combo2Items}"/>
        </p:selectOneMenu>
  • السطر 2: العلامة <h:selectOneMenu> بدون السمة valueChangeListener،
  • السطر 4: العلامة <p:ajax> تضيف سلوكًا AJAX إلى علامتها الأم <h:selectOneMenu>. بشكل افتراضي، تستجيب لحدث "تغيير القيمة" في القائمة combo1. عند حدوث هذا الحدث، سيتم إرسال قيم النموذج الذي تنتمي إليه إلى الخادم عن طريق استدعاء AJAX. وبالتالي يتم تحديث النموذج. نستخدم هذا النموذج الجديد لتحديث القائمة المنسدلة المحددة بواسطة combo2 (السطر 10). يُلاحظ، في السطر 4، أن استدعاء AJAX لا ينفذ أي طريقة من طرق النموذج. وهذا غير ضروري هنا. نريد ببساطة تغيير النموذج بواسطة POST للقيم التي تم إدخالها.

5.12. مثال: mv-pf-09: الإدخال بمساعدة

يقدم هذا المشروع عناصر إدخال خاصة بـ Primefaces تسهل إدخال أنواع معينة من البيانات:

5.12.1. مشروع Netbeans

مشروع Netbeans هو التالي:

تكمن أهمية المشروع في:

  • الصفحة الوحيدة [index.html] التي يعرضها،
  • نموذج [Form.java] لهذه الصفحة.

5.12.2. القالب

يحتوي النموذج على أربعة حقول إدخال مرتبطة بالنموذج التالي:


package forms;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean
@SessionScoped
public class Form implements Serializable {

  private Date calendrier;
  private Integer slider = 100;
  private Integer spinner = 1;
  private String autocompleteValue;

  public Form() {
  }

  public List<String> autocomplete(String query) {
    ...
  }
   // الوصول والتعيين
...
}

ترتبط الإدخالات الأربعة بحقول الأسطر 14-17.

5.12.3. النموذج

النموذج هو التالي:


<?xml version='1.0' encoding='UTF-8' ?>
<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:ui="http://java.sun.com/jsf/facelets">

  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre']}"/></h2>
      <p:growl id="messages" autoUpdate="true"/>
      <p:panelGrid columns="3" columnClasses="col1,col2,col3,col4">
        <h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
        <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>

        <!-- التقويم -->
              ...

        <!-- شريط التمرير -->
              ...

        <!-- عجلة الدوران -->
              ...

        <!-- الإكمال التلقائي -->
              ...

      </p:panelGrid>
    </ui:define>
  </ui:composition>
</html>

دعونا نلقي نظرة على حقول الإدخال الأربعة.

5.12.4. التقويم

تسمح العلامة <p:calendar> باختيار تاريخ من التقويم. تقبل هذه العلامة سمات مختلفة.


<h:outputText value="#{msg['calendar.prompt']}"/>
        <p:calendar id="calendrier" value="#{form.calendrier}" pattern="dd/MM/yyyy" timeZone="Europe/Paris"/>
        <h:outputText id="calendrierValue" value="#{form.calendrier}">
          <f:convertDateTime pattern="dd/MM/yyyy" type="date" timeZone="Europe/Paris"/>
        </h:outputText>

في السطر 2، نحدد أن التاريخ يجب أن يُعرض بالتنسيق "yy/mm/yyyy" وأن المنطقة الزمنية هي منطقة باريس. عند وضع المؤشر في منطقة الإدخال، يتم عرض تقويم:

 

5.12.5. شريط التمرير

تسمح العلامة <p:slider> بإدخال عدد صحيح عن طريق تحريك شريط التمرير على طول شريط:

 

رمز العلامة هو التالي:


        <h:outputText value="#{msg['slider.prompt']}"/>
        <h:panelGrid columns="1" style="margin-bottom:10px">  
          <p:inputText id="slider" value="#{form.slider}" required="true" requiredMessage="#{msg['slider.required']}" validatorMessage="#{msg['slider.invalide']}">  
            <f:validateLongRange minimum="100" maximum="200"/>
          </p:inputText>
          <p:slider for="slider" minValue="100" maxValue="200"/>  
        </h:panelGrid>  
<h:outputText id="sliderValue" value="#{form.slider}"/>
  • السطر 3: لدينا هنا علامة <p:inputText> تقليدية تسمح بإدخال الرقم الصحيح. يمكن أيضًا إدخال هذا الرقم باستخدام شريط التمرير،
  • السطر 4: ترتبط علامة <p:slider> بعلامة الإدخال <p:inputText> (السمة for). يتم تعيين قيمة دنيا وقيمة قصوى لها.

5.12.6. السبينر

سبق أن قدمنا هذا المكون:


        <h:outputText value="#{msg['spinner.prompt']}"/>
        <p:spinner id="spinner" min="1" max="12" value="#{form.spinner}" required="true" requiredMessage="#{msg['spinner.required']}" validatorMessage="#{msg['spinner.invalide']}">
          <f:validateLongRange minimum="1" maximum="12"/>
        </p:spinner>
<h:outputText id="spinnerValue" value="#{form.spinner}"/>

السطر 3، يسمح spinner بإدخال عدد صحيح بين 1 و 12. يمكن إدخال الرقم مباشرة في منطقة الإدخال الخاصة بـ spinner أو استخدام الأسهم لزيادة / تقليل الرقم المدخل.

 

5.12.7. الإدخال المساعد

تتمثل الإدخال المساعد في كتابة الأحرف الأولى من النص المراد إدخاله. ثم تظهر اقتراحات في قائمة منسدلة. يمكن اختيار أحدها. يتم استخدام هذا المكون بدلاً من القوائم المنسدلة عندما يكون محتواها كبيرًا جدًا. لنفترض أننا نريد عرض قائمة منسدلة بمدن فرنسا. هناك عدة آلاف من المدن. إذا تركنا المستخدم يكتب الأحرف الثلاثة الأولى من اسم المدينة، يمكننا عندئذٍ عرض قائمة مختصرة بالمدن التي تبدأ بهذه الأحرف.

 

رمز هذا المكون هو التالي:


        <h:outputText value="#{msg['autocomplete.prompt']}"/>
        <p:autoComplete value="#{form.autocompleteValue}" completeMethod="#{form.autocomplete}" required="true" requiredMessage="#{msg['autocomplete.required']}"/>
        <h:outputText id="autocompleteValue" value="#{form.autocompleteValue}"/>
        <h:panelGroup/>
        <h:panelGroup>
        <center><p:commandLink value="#{msg['valider']}" update="formulaire:contenu"/></center>
        </h:panelGroup>
<h:panelGroup/>

العلامة <p:autoComplete> في السطر 2 هي التي تسمح بالإدخال المساعد. المعلمة التي تهمنا هنا هي السمة completeMethod التي تمثل اسم طريقة في النموذج، مسؤولة عن تقديم اقتراحات تتوافق مع الأحرف التي يكتبها المستخدم. هذه الطريقة هي كما يلي:


  public List<String> autocomplete(String query) {
    List<String> results = new ArrayList<String>();

    for (int i = 0; i < 10; i++) {
      results.add(query + i);
    }

    return results;
}

  • السطر 1: تتلقى الطريقة كمعلمة سلسلة الأحرف التي يكتبها المستخدم في منطقة الإدخال. وتُرجع قائمة من الاقتراحات،
  • الأسطر 4-6: يتم إنشاء قائمة من 10 اقتراحات تتضمن الأحرف المستلمة كمعلمات وتضيف إليها رقمًا من 0 إلى 9.

5.12.8. العلامة <p:growl>

العلامة <p:growl> هي بديل محتمل للعلامة <p:messages> التي تعرض رسائل الخطأ الخاصة بالنموذج.


      <p:growl id="messages" autoUpdate="true"/>

فيما سبق، لم يتم استخدام السمة id. تشير السمة autoUpdate=true إلى أنه يجب تحديث قائمة رسائل الخطأ عند كل POST للنموذج.

لنفترض أننا نقوم بالتحقق من صحة النموذج التالي [1]:

  • في [2]، تعرض علامة <p:growl> رسائل الأخطاء المرتبطة بالبيانات الخاطئة.

5.13. مثال: mv-pf-10: dataTable - 1

يقدم هذا المشروع العلامة <p:dataTable> التي تُستخدم لعرض قوائم البيانات

Image

5.13.1. مشروع NetBeans

مشروع Netbeans هو التالي:

تكمن أهمية المشروع في:

  • الصفحة الوحيدة [index.html] التي يعرضها،
  • والنموذج [Form.java] الخاص بها، والبيان [Personne].

5.13.2. ملف الرسائل

ملف [messages_fr.properties] هو التالي:


app.titre=intro-08
app.titre2=DataTable - 1
submit=Valider
personnes.headers.id=Id
personnes.headers.nom=Nom
personnes.headers.prenom=Pr\u00e9nom
layout.hautdepage=Primefaces en fran\u00e7ais
layout.menu=Menu fran\u00e7ais
layout.basdepage=ISTIA, universit\u00e9 d'Angers
form.langue1=Fran\u00e7ais
form.langue2=Anglais
form.noData=La liste des personnes est vide
form.listePersonnes=Liste de personnes
form.action=Action

5.13.3. القالب

يمثل bean [Personne] شخصًا:


package forms;

import java.io.Serializable;

public class Personne implements Serializable{
  // البيانات
  private int id;
  private String nom;
  private String prénom;
  
  // الشركات المصنعة
  public Personne(){
    
  }
  
  public Personne(int id, String nom, String prénom){
    this.id=id;
    this.nom=nom;
    this.prénom=prénom;
  }
  
  // toString
  public String toString(){
    return String.format("Personne[%d,%s,%s]", id,nom,prénom);
  }
  
  // المُستخرج والمُعيّن
...
}

نموذج الصفحة [index.xhtml] هو الفئة [Form] التالية:


package forms;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class Form implements Serializable{

  // نموذج
  private List<Personne> personnes;
  private int personneId;

  // منشئ
  public Form() {
    // تهيئة قائمة الأشخاص
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }

  public void retirerPersonne() {
...
  }
  
  // الوصول والضبط
...  
}
  • السطران 9-10: نطاق الفول هو الجلسة،
  • السطور 18-24: يقوم المنشئ بإنشاء قائمة من ثلاثة أشخاص، وهي قائمة ستبقى موجودة طوال فترة الطلبات،
  • السطر 15: رقم الشخص المراد حذفه من القائمة،
  • السطور 26-28: طريقة الحذف.

5.13.4. النموذج

النموذج هو التالي [index.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:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}">
        <f:facet name="header">  
           #{msg['form.listePersonnes']}  
        </f:facet>  
        <p:column>
          <f:facet name="header">
             #{msg['personnes.headers.id']}
          </f:facet>
           #{personne.id}
        </p:column>
        <p:column>
          <f:facet name="header">
             #{msg['personnes.headers.nom']}
          </f:facet>
           #{personne.nom}
        </p:column>
        <p:column>
          <f:facet name="header">
             #{msg['personnes.headers.prenom']}
          </f:facet>
           #{personne.prénom}
        </p:column>
        <p:column>
          <f:facet name="header">
             #{msg['form.action']}
          </f:facet>
          <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu">
            <f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
          </p:commandLink>
        </p:column>
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>

ينتج عن ذلك العرض التالي (المربع أدناه):

  • السطر 12: يولد الجدول الموضح أعلاه. يشير السمة value إلى المجموعة المعروضة في الجدول، وهي في هذه الحالة قائمة الأشخاص في النموذج. السمة emptyMessage اختيارية. وهي تشير إلى الرسالة التي سيتم عرضها عندما تكون القائمة فارغة. بشكل افتراضي، تكون الرسالة "no records found". في هذه الحالة، ستكون:
 
  • الأسطر 13-15: تولد العنوان [1]،
  • الأسطر 16-21: تولد العمود [2]،
  • الأسطر 22-27: تولد العمود [3]،
  • الأسطر 28-33: تولد العمود [4]،
  • الأسطر 34-41: تولد العمود [5].

الرابط [Retirer] يسمح بإزالة شخص من القائمة. السطر [38]، الطريقة [Form].retirerPersonne هي التي تقوم بهذه المهمة. وهي تحتاج إلى معرفة رقم الشخص المراد حذفه. ويتم تزويدها بهذا الرقم في السطر 39. في السطر 38، تم استخدام السمة action. وفي حالات أخرى، تم استخدام السمة actionListener. لست متأكدًا من فهمي التمييز الوظيفي بين هاتين السمتين. ولكن عند الاستخدام، نلاحظ أن السمات التي تحددها العلامات <setPropertyActionListener> يتم تحديدها قبل تنفيذ الطريقة المحددة بواسطة السمة action، وأن هذا ليس هو الحال بالنسبة للسمة actionListener. ببساطة، بمجرد أن يكون لدينا معلمات لإرسالها إلى الإجراء الذي تم استدعاؤه، يجب استخدام السمة action.

الطريقة لإزالة شخص ما هي كما يلي:


...
@ManagedBean
@SessionScoped
public class Form implements Serializable{

  // نموذج
  private List<Personne> personnes;
  private int personneId;

  public void retirerPersonne() {
    // نبحث عن الشخص المحدد
    int i = 0;
    for (Personne personne : personnes) {
      // الشخص الحالي = الشخص المحدد؟
      if (personne.getId() == personneId) {
        // حذف الشخص الحالي من القائمة
        personnes.remove(i);
        // انتهينا
        break;
      } else {
        // الشخص التالي
        i++;
      }
    }
  }
...  
}

5.14. مثال: mv-pf-11: dataTable - 2

يعرض هذا المشروع جدولًا يحتوي على قائمة بيانات حيث يمكن تحديد سطر واحد:

يؤدي تحديد سطر من الجدول، عند POST، إلى إرسال معلومات إلى النموذج حول السطر المحدد. وبالتالي، لم يعد هناك حاجة إلى رابط [Retirer] لكل شخص. يكفي رابط واحد للجدول بأكمله.

مشروع Netbeans مطابق للمشروع السابق باستثناء بعض التفاصيل: النموذج والقالب الخاص به. النموذج [index.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:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   rowKey="#{personne.id}"  selection="#{form.personneChoisie}" selectionMode="single">
        ...
      </p:dataTable>
      <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu"/>
    </ui:define>
  </ui:composition>
</html>
  • السطر 13: يسمح السمة selectionMode باختيار وضع التحديد single أو multiple. هنا، اخترنا تحديد سطر واحد فقط،
  • السطر 13: يشير السمة rowkey إلى سمة العناصر المعروضة التي تسمح بتحديدها بشكل فريد. هنا، اخترنا معرف الشخص المحدد،
  • السطر 13: يشير السمة selection إلى سمة النموذج التي ستتلقى مرجعًا للشخص المحدد. بفضل السمة rowkey السابقة، سيتمكن جانب الخادم من حساب مرجع للشخص المحدد. ليس لدينا تفاصيل عن الطريقة المستخدمة. يمكننا أن نتصور أن المجموعة يتم تصفحها بالتسلسل بحثًا عن العنصر المطابق لـ rowkey المحدد. هذا يعني أنه إذا كانت الطريقة التي تربط rowkey بـ selection أكثر تعقيدًا، فإن هذه الطريقة غير قابلة للاستخدام،

وبناءً على ذلك، تتطور الطريقة [Form].retirerPersonne على النحو التالي:


...

@ManagedBean
@SessionScoped
public class Form implements Serializable {

  // نموذج
  private List<Personne> personnes;
  private Personne personneChoisie;

  // الشركة المصنعة
  public Form() {
  ...
  }

  public void retirerPersonne() {
    // إزالة الشخص المحدد
    personnes.remove(personneChoisie);
  }

  // المُحصلات والمُعيّنات
...
}
  • السطر 9: عند كل POST، يتم تعيين مرجع السطر 9 بقيمة المرجع الخاص بالشخص المحدد في قائمة السطر 8،
  • في 18: يتم تبسيط عملية حذف الشخص. تم إجراء البحث الذي قمنا به في المثال السابق بواسطة العلامة <dataTable>.

5.15. مثال: mv-pf-12: dataTable - 3

هذا المشروع مشابه للمشروع السابق. العرض متطابق بشكل خاص:

Image

مشروع Netbeans مطابق للمشروع السابق باستثناء بعض التفاصيل التي سنستعرضها. يتطور النموذج [index.xhtml] على النحو التالي:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   selectionMode="single" selection="#{form.personneChoisie}">
  ...
      </p:dataTable>
      <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu"/>
    </ui:define>
  </ui:composition>
</html>
  • السطر 6، اختفى السمة rowkey، وبقيت السمة selection. يتم الآن الربط بين السمتين rowkey و selection من خلال فئة. أصبح السمة value في السطر 5 الآن لها قيمة مثيل واجهة Primefaces SelectableDataModel<T>. تتطور طريقة [Form].getPersonnes في النموذج على النحو التالي:

  public DataTableModel getPersonnes() {
    return new DataTableModel(personnes);
}

وبذلك يتم إضافة bean جديد إلى المشروع:

وهذا الكائن هو التالي:


package forms;

import java.util.List;
import javax.faces.model.ListDataModel;
import org.primefaces.model.SelectableDataModel;

public class DataTableModel extends ListDataModel<Personne> implements SelectableDataModel<Personne> {

  // المنشئون
  public DataTableModel() {
  }

  public DataTableModel(List<Personne> personnes) {
    super(personnes);
  }

  @Override
  public Object getRowKey(Personne personne) {
    return personne.getId();
  }

  @Override
  public Personne getRowData(String rowKey) {
    // قائمة الأشخاص
    List<Personne> personnes = (List<Personne>) getWrappedData();
    // المفتاح هو عدد صحيح 
    int key = Integer.parseInt(rowKey);
    // البحث عن الشخص المحدد
    for (Personne personne : personnes) {
      if (personne.getId() == key) {
        return personne;
      }
    }
    // لم يتم العثور على شيء
    return null;
  }
}
  • السطر 7: الفئة هي مثيل للواجهة SelectableDataModel. هناك فئتان على الأقل تنفذان هذه الواجهة: ListDataModel التي يقبل منشئها قائمة كمعلمة، و ArrayDataModel التي يقبل منشئها مصفوفة كمعلمة. هنا، يوسع bean الخاص بنا الفئة ListDataModel،
  • الأسطر 13-15: يقبل المنشئ كمعلمة قائمة الأشخاص الذين نديرهم. يتم تمرير هذه المعلمة إلى الفئة الأم،
  • السطر 18: تلعب الطريقة getRowKey دور السمة rowkey التي تمت إزالتها. يجب أن تُرجع الكائن الذي يسمح بتحديد هوية شخص ما بشكل فريد، وهنا معرّف الشخص،
  • السطر 23: يجب أن تعرض الطريقة getRowData الكائن المحدد، بناءً على rowkey الخاص به. لذا هنا، عرض شخص بناءً على معرّفه. سيتم تعيين المرجع الذي تم الحصول عليه بهذه الطريقة إلى الكائن المستهدف للسمة selection في العلامة dataTable، وهنا السمة selection="#{form.personneChoisie}". معلمة الطريقة هي rowkey للكائن الذي اختاره المستخدم، في شكل سلسلة أحرف،
  • السطور 24-35: توفر المرجع الخاص بالشخص الذي تم استلام معرّفه. سيتم تعيين هذا المرجع إلى النموذج [Form].personneChoisie. وبالتالي، تظل الطريقة [retirerPersonne] دون تغيير:

  public void retirerPersonne() {
    // إزالة الشخص المختار
    personnes.remove(personneChoisie);
  }

هذه هي التقنية التي يجب استخدامها عندما لا يكون الارتباط بين السمتين rowkey و selection مجرد ارتباط بين خاصية (rowkey) وكائن (selection).

5.16. مثال: mv-pf-13: dataTable - 4

هذا المشروع مشابه للمشروع السابق باستثناء أن طريقة اختيار الشخص المراد حذفه تتغير:

Image

فيما سبق، نرى أن الكائن يتم تحديده من خلال قائمة سياقية (النقر بزر الماوس الأيمن). يُطلب تأكيد الحذف:

 

يتطور النموذج [index.xhtml] على النحو التالي:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">

      <!-- العنوان -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>

      <!-- القائمة السياقية -->
      <p:contextMenu for="personnes">  
        <p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
      </p:contextMenu>  

      <!-- مربع الحوار -->
      <p:confirmDialog widgetVar="confirmation" message="#{msg['form.suppression.confirmation']}"  
                       header="#{msg['form.suppression.message']}" severity="alert" >                   
        <p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu"                          action="#{form.retirerPersonne}" oncomplete="confirmation.hide()"/>
        <p:commandButton value="#{msg['form.supprimer.non']}" onclick="confirmation.hide()" type="button" />                
      </p:confirmDialog>  

      <!-- dataTable-->
      <p:dataTable id="personnes" value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   selection="#{form.personneChoisie}" selectionMode="single">
        ...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • الأسطر 9-11: تحدد قائمة سياقية لـ (السمة for) dataTable في السطر 21 (السمة id). وبالتالي، تظهر هذه القائمة السياقية عند النقر بزر الماوس الأيمن على جدول الأشخاص،
  • السطر 10: تحتوي قائمتنا على خيار واحد فقط (العلامة menuItem). عند النقر على هذا الخيار، يتم تنفيذ كود جافا سكريبت الخاص بالسمة onclick. يعرض كود جافا سكريبت [confirmation.show()] مربع الحوار الخاص بالسطر 14 (السمة widgetVar). وهو كما يلي:
  • السطر 14: يعرض السمة message [3]، ويعرض السمة header [1]، ويعرض السمة severity الرمز [2]،
  • السطر 16: يعرض [4]. عند النقر، يتم حذف الشخص (السمة action)، ثم يتم إغلاق مربع الحوار (السمة oncomplete). السمة oncomplete هي كود جافا سكريبت يتم تنفيذه بمجرد تنفيذ الإجراء من جانب الخادم،
  • السطر 17: يعرض [5]. عند النقر، يتم إغلاق مربع الحوار ولا يتم حذف الشخص.

5.17. مثال: mv-pf-14: dataTable - 5

يوضح هذا المشروع أنه من الممكن الحصول على استجابة من الخادم بعد تنفيذ استدعاء AJAX. وللقيام بذلك، يتم استخدام السمة oncomplete الخاصة باستدعاء AJAX:

 

يتطور النموذج [index.xhtml] على النحو التالي:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
...
<!-- مربع الحوار 1 -->
      <p:confirmDialog widgetVar="confirmation" ... >                   
        <p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu" action="#{form.retirerPersonne}" oncomplete="handleRequest(xhr, status, args);confirmation.hide()"/>
        <p:commandButton ... />                
      </p:confirmDialog>  

      <!-- جافا سكريبت -->
      <script type="text/javascript">  
        function handleRequest(xhr, status, args) {  
          // خطأ؟
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
      </script> 
...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • السطر 7: تستدعي السمة oncomplete وظيفة Javascript في الأسطر 13-18،
  • السطر 13: يجب أن تكون توقيع الأسلوب كما يلي. args هو قاموس يمكن للنموذج على جانب الخادم إثرائه،
  • السطر 15: يتم التحقق مما إذا كان القاموس args يحتوي على سمة باسم 'msgErreur'. إذا كان الأمر كذلك، يتم عرضها (السطر 16).

في النموذج، تتطور الطريقة [retirerPersonne] على النحو التالي:


public void retirerPersonne() {
    // حذف عشوائي
    int i = (int) (Math.random() * 2);
    if (i == 0) {
      // إزالة الشخص المختار
      personnes.remove(personneChoisie);
    } else {
      // إرجاع خطأ
      String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
      RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
    }
  }
  • السطر 3: يتم سحب رقم عشوائي 0 أو 1،
  • الأسطر 4-6: إذا كان الرقم 0، يتم حذف الشخص الذي اختاره المستخدم من قائمة الأشخاص،
  • السطر 9: وإلا، يتم إنشاء رسالة خطأ دولية:

form.msgErreur=La personne n'a pu \u00eatre supprim\u00e9e. Veuillez r\u00e9essayer ult\u00e9rieurement.
form.msgErreur_detail=La personne n'a pu \u00eatre supprim\u00e9e. Veuillez r\u00e9essayer ult\u00e9rieurement.
  • السطر 10: تعليمة معقدة تهدف إلى إضافة السمة المسماة 'msgErreur' إلى قاموس args الذي تحدثنا عنه، مع القيمة msgErreur التي تم إنشاؤها في السطر 9. يتم استرداد هذا السمة بعد ذلك بواسطة طريقة جافا سكريبت لـ [index.xhtml]:

      <!-- جافا سكريبت -->
      <script type="text/javascript">  
        function handleRequest(xhr, status, args) {  
          // خطأ؟
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
</script> 

5.18. مثال: mv-pf-15: شريط الأدوات

في هذا المشروع، نقوم بإنشاء شريط أدوات:

شريط الأدوات هو المكون المحدد أعلاه. يتم الحصول عليه باستخدام الكود XHTML التالي [index.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:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <!-- العنوان -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>

      <!-- شريط الأدوات-->
      <p:toolbar>
        <p:toolbarGroup align="left">  
          ...  
        </p:toolbarGroup>
        <p:toolbarGroup align="right">  
          ...  
        </p:toolbarGroup>  
      </p:toolbar>
    </ui:define>
  </ui:composition>
</html>
  • الأسطر 15-22: شريط الأدوات،
  • الأسطر 16-18: تحدد مجموعة المكونات الموجودة على يسار الشريط،
  • الأسطر 19-21: نفس الشيء بالنسبة للمكونات الموجودة على اليمين.

المكونات الموجودة على يسار شريط الأدوات هي التالية:


        <p:toolbarGroup align="left">  
          <h:outputText value="#{msg['form.etudiant']}"/>
          <p:spacer width="50px"/>
          <p:selectOneMenu value="#{form.personneId}" effect="fade">  
            <f:selectItems value="#{form.personnes}" var="personne" itemLabel="#{personne.prénom} #{personne.nom}" itemValue="#{personne.id}"/>  
          </p:selectOneMenu>              
          <p:separator/>
          <p:commandButton id="delete-personne" icon="ui-icon-trash" action="#{form.supprimerPersonne}" update=":formulaire:contenu"/>  
          <p:tooltip for="delete-personne" value="#{msg['form.delete.personne']}"/>  
</p:toolbarGroup>

وهي تعرض المنظر التالي:

  • السطر 2: يعرض [1]،
  • السطر 3: يعرض مسافة تبلغ 30 بكسل [2]،
  • السطور 4-6: تعرض قائمة منسدلة تحتوي على قائمة بالأشخاص [3]،
  • السطر 7: يعرض فاصل [4]،
  • السطر 8: يعرض زرًا [5] مسؤولاً عن حذف الشخص المحدد في القائمة المنسدلة. يحتوي الزر على أيقونة. هذه الأيقونات هي JQuery UI. توجد قائمة بها في URL [http://jqueryui.com/themeroller/] [6]:
  • لمعرفة اسم أيقونة ما، يكفي تمرير الماوس فوقها. بعد ذلك، يتم استخدام هذا الاسم في سمة icon للمكون <commandButton>، على سبيل المثال icon="ui-icon-trash". تجدر الإشارة إلى أن الاسم المحدد أعلاه سيكون .ui-icon-trash وأنه يتم حذف النقطة الأولى من هذا الاسم في السمة icon,
  • السطر 9: ينشئ فقاعة مساعدة للزر (السمة for). عند ترك المؤشر على الزر، تظهر رسالة المساعدة [7].

القالب المرتبط بهذه المكونات هو التالي:


package forms;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class Form implements Serializable {

  // قالب
  private List<Personne> personnes;
  private int personneId;

  // المصمم
  public Form() {
    // تهيئة قائمة الأشخاص
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }

  public void supprimerPersonne() {
    // البحث عن الشخص المحدد
    int i = 0;
    for (Personne personne : personnes) {
      // الشخص الحالي = الشخص المحدد؟
      if (personne.getId() == personneId) {
        // حذف الشخص الحالي من القائمة
        personnes.remove(i);
        // انتهينا
        break;
      } else {
        // الشخص التالي
        i++;
      }
    }
  }

  // مُستردات ومُعيّنات
  ...
}

المكونات الموجودة على يمين شريط الأدوات هي التالية:


<p:toolbar>
        <p:toolbarGroup align="left">  
          ... 
        </p:toolbarGroup>
        <p:toolbarGroup align="right">  
          <p:menuButton value="#{msg['form.options']}">  
            <p:menuitem id="menuitem-francais" value="#{msg['form.francais']}" actionListener="#{changeLocale.setFrenchLocale}" update=":formulaire"/>  
            <p:menuitem id="menuitem-anglais" value="#{msg['form.anglais']}" actionListener="#{changeLocale.setEnglishLocale}" update=":formulaire"/>  
          </p:menuButton>  
        </p:toolbarGroup>  
      </p:toolbar>

وهي تعرض المنظر التالي:

 
  • السطور 6-9: زر قائمة. يحتوي على خيارات القائمة،
  • السطر 7: خيار لتغيير لغة النموذج إلى الفرنسية،
  • السطر 8: خيار تحويله إلى اللغة الإنجليزية.

5.19. خلاصة

لدينا الآن ما يكفي من المعرفة لنقل تطبيقنا النموذجي إلى Primefaces. لم نر سوى حوالي خمسة عشر مكونًا، في حين أن المكتبة تحتوي على أكثر من 100 مكون. ندعو القارئ للبحث عن المكون الذي يفتقده مباشرةً على موقع Primefaces.

5.20. الاختبارات باستخدام Eclipse

مشاريع Maven متاحة على موقع الأمثلة [1]:

بمجرد استيرادها إلى Eclipse، يمكن تشغيلها [2]. نختار Tomcat في [3]. ثم يتم عرضها في متصفح Eclipse الداخلي [3].