Skip to content

2. Java Server Faces

اکنون چارچوب Java Server Faces را معرفی می‌کنیم. ما از نسخه ۲ استفاده خواهیم کرد، اما مثال‌ها عمدتاً ویژگی‌های نسخه ۱ را نشان می‌دهند. ما فقط به آن ویژگی‌های نسخه ۲ می‌پردازیم که برای اپلیکیشن مثالی بعدی ضروری هستند.

2.1. نقش JSF در یک برنامه وب

ابتدا، بیایید نقش JSF را در توسعه یک برنامه وب در نظر بگیریم. در اکثر موارد، این برنامه بر روی یک معماری چندلایه مانند موارد زیر ساخته خواهد شد:

  • لایه [web] لایه‌ای است که با کاربر وب‌اپلیکیشن ارتباط برقرار می‌کند. کاربر از طریق صفحات وبی که در یک مرورگر نمایش داده می‌شوند، با وب‌اپلیکیشن تعامل دارد. JSF در این لایه قرار دارد، و تنها در همین لایه؛
  • لایه [métier] قواعد کسب‌وکار برنامه، مانند محاسبه حقوق یا فاکتور را پیاده‌سازی می‌کند. این لایه داده‌ها را از طریق لایه [web] از کاربر و از طریق لایه [DAO] از سیستم مدیریت پایگاه داده (DBMS) استفاده می‌کند،
  • لایه [DAO] (ابجکت‌های دسترسی به داده)، لایه [jpa] (Java Persistence API) و درایور JDBC دسترسی به داده‌های DBMS را مدیریت می‌کنند. لایه [jpa] به‌عنوان یک نگاشتگر شیء-رابطه‌ای (ORM) عمل می‌کند. این لایه شکاف بین اشیاء مدیریت‌شده توسط لایه [DAO] و سطر و ستون‌های داده در یک پایگاه داده رابطه‌ای را پر می‌کند؛
  • یکپارچه‌سازی لایه‌ها را می‌توان با استفاده از یک کانtejner Spring یا EJB3 (Enterprise Java Bean) انجام داد.

مثال‌های ارائه‌شده در زیر برای تشریح JSF تنها از یک لایه، یعنی لایه [web]، استفاده خواهند کرد:

پس از تسلط بر مبانی JSF، ما با استفاده از EE، برنامه‌های چندلایه جاوا را خواهیم ساخت.

2.2. مدل توسعه MVC از JSF

JSF الگوی معماری موسوم به MVC (مدل–نما–کنترل‌کننده) را به شرح زیر پیاده‌سازی می‌کند:

این معماری الگوی طراحی MVC (مدل، نما، کنترل‌کننده) را پیاده‌سازی می‌کند. پردازش یک درخواست مشتری در چهار مرحله زیر انجام می‌شود:

  1. درخواست – مرورگر مشتری یک درخواست را به کنترل‌کننده [Faces Servlet] ارسال می‌کند. این کنترل‌کننده تمام درخواست‌های مشتری را مدیریت می‌کند. این نقطه ورود به برنامه است. این C در MVC است،
  2. پردازش – کنترل‌کننده C این درخواست را پردازش می‌کند. برای این کار، از رویدادپردازهای مخصوص برنامه که به صورت [2a] نوشته شده‌اند، کمک می‌گیرد. این رویدادپردازها ممکن است به کمک لایه کسب‌وکار، [2b]، نیاز داشته باشند. پس از پردازش درخواست مشتری، ممکن است پاسخ‌های مختلفی ایجاد شود. یک مثال معمول عبارت است از:
    • یک صفحه خطا اگر درخواست نتوانست به درستی پردازش شود؛
    • در غیر این صورت، یک صفحه تأیید.
  3. ناوبری – کنترل‌کننده پاسخ (= نما) ارسالی به کلاینت را انتخاب می‌کند. انتخاب پاسخ ارسالی به کلاینت شامل چندین مرحله است:
    • انتخاب Facelet که پاسخ را تولید خواهد کرد. این به عنوان نمای V شناخته می‌شود، V در MVC. این انتخاب عموماً به نتیجه اجرای عملی که توسط کاربر درخواست شده است بستگی دارد؛
    • تأمین داده‌های مورد نیاز این فلیت برای تولید این پاسخ. در واقع، این پاسخ اغلب حاوی اطلاعاتی است که توسط کنترل‌کننده محاسبه شده است. این اطلاعات همان چیزی را تشکیل می‌دهد که به آن مدل M نما (view's model M) گفته می‌شود، M در MVC،

مرحله ۳ بنابراین شامل انتخاب یک نما V و ساخت مدل M مورد نیاز برای آن است.

  1. پاسخ – کنترل‌کننده C به Facelet انتخاب‌شده دستور می‌دهد تا خود را رندر کند. Facelet از مدل M که توسط کنترل‌کننده C آماده شده است برای راه‌اندازی بخش‌های پویا از پاسخی که باید به کلاینت ارسال کند، استفاده می‌کند. قالب دقیق این پاسخ ممکن است متفاوت باشد: می‌تواند یک جریان HTML، PDF، اکسل و غیره باشد.

در یک پروژه JSF:

  • کنترل‌کننده C، سرولِت [javax.faces.webapp.FacesServlet] است. این سرولِت در کتابخانه [javaee.jar] قرار دارد،
  • ویوهای V با استفاده از صفحات مبتنی بر فناوری Facelets پیاده‌سازی شده‌اند،
  • مدل‌های M و رویدادپردازها توسط کلاس‌های جاوا پیاده‌سازی می‌شوند که اغلب به آن‌ها «بیان‌های پشتیبان» یا به سادگی «بیان‌ها» گفته می‌شود.

اکنون، بیایید ارتباط بین معماری وب MVC و معماری لایه‌ای را روشن کنیم. این دو مفهوم متفاوت هستند که گاهی با یکدیگر اشتباه گرفته می‌شوند. بیایید یک برنامه وب تک‌لایه JSF را در نظر بگیریم:

اگر لایه [web] را با استفاده از JSF پیاده‌سازی کنیم، در واقع یک معماری وب MVC خواهیم داشت، اما یک معماری چندلایه نخواهیم داشت. در اینجا لایه [web] همه چیز را مدیریت می‌کند: ارائه، منطق کسب‌وکار و دسترسی به داده‌ها. با JSF، بین‌ها این کار را انجام می‌دهند.

اکنون، بیایید یک معماری وب چندلایه را در نظر بگیریم:

لایه [web] می‌تواند بدون فریم‌ورک و بدون پیروی از مدل MVC پیاده‌سازی شود. بنابراین ما در واقع یک معماری چندلایه داریم، اما لایه وب مدل MVC را پیاده‌سازی نمی‌کند.

در MVC بیان کردیم که مدل M همان نمای V است، c.a.d: مجموعه‌ای از داده‌هایی که توسط نمای V نمایش داده می‌شوند. تعریف دیگری از مدل M در MVC اغلب ارائه می‌شود:

بسیاری از نویسندگان معتقدند که آنچه در سمت راست لایه [web] قرار دارد، مدل M از MVC را تشکیل می‌دهد. برای جلوگیری از ابهام، ما به موارد زیر اشاره خواهیم کرد:

  • مدل دامنه زمانی که به همه چیز در سمت راست لایه [web] اشاره می‌شود،
  • مدل نماهنگامی که به داده‌های نمایش‌داده‌شده توسط یک نما V اشاره می‌شود.

از این پس، اصطلاح «مدل M» منحصراً به مدل یک نما V اشاره خواهد داشت.

2.3. مثال mv-jsf2-01: عناصر یک پروژه JSF

نخستین مثال‌ها محدود به یک لایه وب واحد خواهند بود که با JSF 2 پیاده‌سازی شده است:

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

2.3.1. تولید پروژه

ما اولین پروژه خود، JSF2، را با استفاده از NetBeans 7 ایجاد می‌کنیم.

  
  • در [1]، یک پروژه جدید ایجاد کنید،
  • در [2]، دسته‌بندی [Maven] و نوع پروژه [Web Application] را انتخاب کنید،
  • در [3]، پوشهٔ والد برای پوشهٔ پروژهٔ جدید را مشخص کنید،
  • در [4]، برای پروژه یک نام انتخاب کنید،
  • در [5]، یک سرور را انتخاب کنید. با NetBeans 7، می‌توانید بین سرورهای Apache Tomcat و GlassFish انتخاب کنید. تفاوت بین این دو آن است که GlassFish از EJB (Enterprise Java Beans) پشتیبانی می‌کند، در حالی که Tomcat این کار را انجام نمی‌دهد. نمونه‌های JSF ما از EJB استفاده نخواهند کرد. بنابراین در اینجا می‌توانید هر سروری را انتخاب کنید،
  • در [6]، نسخه جاوا EE 6 Web را انتخاب می‌کنیم،
  • و در [7]، پروژهٔ تولیدشده.

اجزای پروژه را بررسی کرده و نقش هر یک را توضیح می‌دهیم.

  • در [1]: شاخه‌های مختلف پروژه:
    • [Web Pages]: شامل صفحات وب (.xhtml، .jsp، .html)، منابع (تصاویر، اسناد مختلف)، پیکربندی لایه وب و پیکربندی فریم‌ورک JSF؛
    • [Source packages]: کلاس‌های جاوا پروژه؛
    • [Dependencies]: آرشیوهای .jar مورد نیاز پروژه که توسط فریم‌ورک Maven مدیریت می‌شوند؛
    • [Java Dependencies]: فایل‌های .jar مورد نیاز پروژه که توسط فریم‌ورک Maven مدیریت نمی‌شوند؛
    • [Project Files]: فایل پیکربندی Maven و NetBeans،
  • در [2]: شاخه [Web Pages

این شامل صفحه زیر است: [index.jsp]:


<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/HTML4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

این یک صفحه وب است که رشته 'Hello World' را با حروف بزرگ نمایش می‌دهد.

فایل [META-INF/context.xml] به شرح زیر است:


<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/mv-jsf2-01"/>

خط ۲ نشان می‌دهد که زمینهٔ برنامه (یا نام آن) /mv-jsf2-01 است. این بدان معناست که صفحات وب پروژه از طریق یک URL به شکل http://machine:port/mv-jsf2-01/page درخواست خواهند شد. زمینه به‌طور پیش‌فرض نام پروژه است. ما نیازی به تغییر این فایل نخواهیم داشت.

  • به [3]، شاخه [Source Packages

این شاخه حاوی کد منبع کلاس‌های جاوا پروژه است. در حال حاضر هیچ کلاسی در اینجا وجود ندارد. NetBeans یک بستهٔ پیش‌فرض به نام [4] ایجاد کرده است که می‌توان آن را حذف کرد.

  • در [5]، شاخه [Dependencies

این شاخه تمام کتابخانه‌های مورد نیاز پروژه را که توسط Maven مدیریت می‌شوند، نمایش می‌دهد. تمام کتابخانه‌های فهرست شده در اینجا به طور خودکار توسط Maven دانلود خواهند شد. به همین دلیل است که یک پروژه Maven به دسترسی به اینترنت نیاز دارد. کتابخانه‌های دانلود شده به صورت محلی ذخیره می‌شوند. اگر پروژه‌ای دیگر به کتابخانه‌ای نیاز داشته باشد که از قبل به صورت محلی موجود است، آن کتابخانه دانلود نخواهد شد. در ادامه خواهیم دید که این فهرست کتابخانه‌ها، به همراه مخازنی که در آن‌ها یافت می‌شوند، در فایل پیکربندی پروژه Maven تعریف شده است.

  • در [6]، کتابخانه‌هایی که پروژه به آنها نیاز دارد اما توسط Maven مدیریت نمی‌شوند،
  • در [7]، فایل‌های پیکربندی پروژه Maven:
    • [nb-configuration.xml] فایل پیکربندی NetBeans است. ما به آن نمی‌پردازیم.
    • [pom.xml]: فایل پیکربندی Maven. POM مخفف Project Object Model است. ممکن است گاهی لازم باشد این فایل را مستقیماً ویرایش کنیم.

فایل تولیدشده [pom.xml] به شرح زیر است:


<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-jsf2-01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mv-jsf2-01</name>

  <properties>
    <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <compilerArguments>
            <endorseddirs>${endorsed.dir}</endorseddirs>
          </compilerArguments>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.1</version>
        <executions>
          <execution>
            <phase>validate</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <outputDirectory>${endorsed.dir}</outputDirectory>
              <silent>true</silent>
              <artifactItems>
                <artifactItem>
                  <groupId>javax</groupId>
                  <artifactId>javaee-endorsed-api</artifactId>
                  <version>6.0</version>
                  <type>jar</type>
                </artifactItem>
              </artifactItems>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>
  • خطوط ۵ تا ۸ شیء جاوا (آرتیفکت) را که توسط پروژه Maven ایجاد می‌شود، تعریف می‌کنند. این اطلاعات از جادوگری که هنگام ایجاد پروژه استفاده می‌شود، می‌آید:

یک شیء Maven با چهار ویژگی تعریف می‌شود:

  • [groupId]: اطلاعاتی که شبیه نام بسته است. بنابراین، کتابخانه‌های فریم‌ورک Spring دارای groupId=org.springframework هستند، در حالی که کتابخانه‌های فریم‌ورک QZXW2HTML دارای JSF=groupId=javax.faces هستند،
  • [artifactId]: نام شیء Maven. در گروه [org.springframework]، بنابراین موارد زیر را می‌یابیم: artifactId، spring-context، spring-core، spring-beans، … در گروه [javax.faces]، ما artifactId و jsf-api, را می‌یابیم
  • [version]: شماره نسخهٔ آرتیفکت Maven. بنابراین، آرتیفکت org.springframework.spring-core نسخه‌های زیر را دارد: 2.5.4، 2.5.5، 2.5.6، 2.5.6.SECO1، ...
  • [packaging]: قالب آرتفکت، که معمولاً war یا jar است.

بنابراین پروژه Maven ما یک [war] (خط ۸) را در گروه [istia.st] (خط ۵) تولید خواهد کرد که با نام [mv-jsf2-01] (خط ۶) و با نسخه [1.0-SNAPSHOT] (خط ۷). این چهار مورد اطلاعات باید به‌طور یکتا یک آرتیفکت Maven را شناسایی کنند.

خطوط 17–24 وابستگی‌های پروژه Maven را فهرست می‌کنند، یعنی فهرست کتابخانه‌های مورد نیاز پروژه. هر کتابخانه با چهار مورد اطلاعات (groupId، artifactId، نسخه، بسته‌بندی) تعریف می‌شود. هنگامی که اطلاعات packaging، مانند اینجا، وجود نداشته باشد، از JAR packaging استفاده می‌شود. ما یک اطلاعات دیگر به نام «دامنه» (scope) اضافه می‌کنیم که مشخص می‌کند کتابخانه در کدام مراحل چرخه عمر پروژه مورد نیاز است. مقدار پیش‌فرض «کامپایل» (compile) است که نشان می‌دهد کتابخانه هم برای کامپایل و هم برای اجرا مورد نیاز است. مقدار «ارائه‌شده» (provided) به این معنی است که کتابخانه در زمان کامپایل مورد نیاز است اما در زمان اجرا نه. در اینجا، در زمان اجرا، توسط سرور Tomcat 7 ارائه خواهد شد.

2.3.2. اجرای پروژه

پروژه را اجرا می‌کنیم:

در [1]، پروژه Maven اجرا می‌شود. سپس سرور Tomcat در صورتی که از قبل در حال اجرا نباشد، راه‌اندازی می‌شود. یک مرورگر نیز باز شده و صفحه URL از زمینه پروژه درخواست می‌شود ([2]). از آنجایی که هیچ سندی درخواست نشده است، در صورتی که وجود داشته باشد، از صفحه index.html، index.jsp، index.xhtml استفاده می‌شود. در این مورد، این صفحه [index.jsp] خواهد بود.

2.3.3. سیستم فایل یک پروژه Maven

  • [1]: سیستم فایل پروژه در برگه [Files] قرار دارد،
  • [2]: فایل‌های منبع جاوا در پوشه [src / main / java] قرار دارند،
  • [3]: صفحات وب در پوشه [src / main / webapp] قرار دارند،
  • [4]: پوشه [target] توسط ساخت پروژه ایجاد شده است،
  • [5]: در اینجا، ساخت پروژه آرشیوی به نام [mv-jsf2-01-1.0-SNAPSHOT.war] ایجاد کرده است. این آرشیو توسط سرور Tomcat اجرا شد.

2.3.4. پیکربندی پروژه برای JSF

پروژه فعلی ما یک پروژه JSF نیست. کتابخانه‌های فریم‌ورک JSF در آن وجود ندارد. برای تبدیل پروژه فعلی به یک پروژه JSF، مراحل زیر را دنبال کنید:

  • در [1]، ویژگی‌های پروژه را باز کنید،
  • در [2]، دسته [Frameworks] را انتخاب کنید،
  • در [3]، یک فریم‌ورک اضافه کنید،
  • در [4]، ما Java Server Faces را انتخاب می‌کنیم،
  • در [5]، NetBeans نسخهٔ ۲.۱ از فریم‌ورک را ارائه می‌دهد. ما این را می‌پذیریم،
  • در [6]، پروژه سپس با وابستگی‌های جدید تقویت می‌شود.

فایل [pom.xml] برای منعکس کردن این پیکربندی جدید به‌روزرسانی شده است:


<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-jsf2-01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mv-jsf2-01</name>

  ...
  <dependencies>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.1.1-b04</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.1.1-b04</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    ...
  </build>
  <repositories>
    <repository>
      <URL>http://download.java.net/maven/2/</URL>
      <id>jsf20</id>
      <layout>default</layout>
      <name>Repository for library Library[jsf20]</name>
    </repository>
    <repository>
      <URL>http://repo1.maven.org/maven2/</URL>
      <id>jstl11</id>
      <layout>default</layout>
      <name>Repository for library Library[jstl11]</name>
    </repository>
  </repositories>
</project>

خطوط 14–33: وابستگی‌های جدید اضافه شده‌اند. Maven آن‌ها را به‌طور خودکار دانلود می‌کند. این فایل‌ها را از آنچه به‌عنوان مخازن (repositories) شناخته می‌شوند، بازیابی می‌کند. مخزن مرکزی (Central Repository) به‌صورت پیش‌فرض استفاده می‌شود. مخازن اضافی را می‌توان با استفاده از تگ <repository> اضافه کرد. در اینجا، دو مخزن اضافه شده‌اند:

  • خطوط ۴۶–۵۱: یک مخزن برای کتابخانه JSF
  • خطوط ۵۲–۵۷: یک مخزن برای کتابخانه JSTL 1.1.

یک صفحه وب جدید نیز به پروژه اضافه شده است:

صفحه [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">
  <h:head>
    <title>Facelet Title</title>
  </h:head>
  <h:body>
    Hello from Facelets
  </h:body>
</html>

در اینجا یک فایل XML (خط ۱) داریم. این فایل شامل تگ‌های HTML اما در قالب XML است. این به عنوان XHTML نامیده می‌شود. فناوری مورد استفاده برای ایجاد صفحات وب با JSFFacelets نام دارد. در نتیجه، صفحه XHTML گاهی اوقات به عنوان یک صفحه Facelet نامیده می‌شود.

خطوط ۳–۴ تگ <html> را با فضای نام XML (xmlns=XML Name Space) تعریف می‌کنند.

  • خط ۳ فضای نام اصلی http://www.w3.org/1999/xhtml را تعریف می‌کند،
  • خط ۴ فضای نام http://java.sun.com/jsf/html را برای تگ‌های HTML تعریف می‌کند. این تگ‌ها با پیش‌وند «h:» مطابق با xmlns:h مشخص می‌شوند. این تگ‌ها را می‌توان در خطوط ۵، ۷، ۸ و ۱۰ یافت.

هنگام برخورد با یک اعلان فضای نام، وب‌سرور در پوشه‌های [META-INF] و Classpath درون برنامه به دنبال فایل‌هایی با پسوند .tld (تعریف TagLib) خواهد گشت. در اینجا، آن‌ها را در آرشیو [jsf-impl.jar] [1,2] پیدا خواهد کرد:

بیایید فایل‌های [3] و [HTML_basic.tld] را بررسی کنیم:

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

<taglib xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1">

<!-- ============== توضیحات کتابخانه تگ: عناصر ============= -->

    <description>
        This tag library contains JavaServer Faces component tags for all
        UIComponent + HTML RenderKit Renderer combinations defined in the
        JavaServer Faces Specification.
    </description>
    <tlib-version>
        2.1
    </tlib-version>
    <short-name>
        h
    </short-name>
    <uri>
        http://java.sun.com/jsf/html
    </uri>

<!-- ============== اعتبارسنج کتابخانه تگ ============= -->
...
  • در خط ۱۹، URI کتابخانه تگ،
  • در خط 16، نام کوتاه آن.

تعاریف تگ‌های مختلف <h:xx> در این فایل یافت می‌شوند. این تگ‌ها توسط کلاس‌های جاوا مدیریت می‌شوند که آن‌ها نیز در شئ [jsf-impl.jar] یافت می‌شوند.

بیایید به پروژه JSF خود بازگردیم. یک شاخه جدید اضافه شده است:

شعبه [Other Sources] [1] حاوی فایل‌هایی است که باید در مسیر کلاس (classpath) پروژه باشند و کد جاوا نیستند. این موضوع در مورد فایل‌های پیام در JSF نیز صدق می‌کند. ما دیدیم که بدون افزودن فریم‌ورک JSF به پروژه، این شاخه وجود ندارد. برای ایجاد آن، کافی است پوشه [src / main / resources] [3] را در برگه [Files] [2] ایجاد کنید.

در نهایت، یک پوشه جدید در شاخه [Web Pages] ظاهر شده است:

پوشه [WEB-INF] ایجاد شد که شامل فایل [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>
    <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>
</web-app>
  • خطوط ۷–۱۰ یک سروِلت به نام c.a.d را تعریف می‌کنند، یک کلاس جاوا که قادر به رسیدگی به درخواست‌های مشتری است. یک برنامه JSF به شرح زیر عمل می‌کند:

این معماری الگوی طراحی MVC (مدل، نما، کنترل‌کننده) را پیاده‌سازی می‌کند. آنچه پیش‌تر بیان شده است را تکرار می‌کنیم. پردازش یک درخواست مشتری در چهار مرحله زیر انجام می‌شود:

۱ – درخواست – مرورگر مشتری یک درخواست را به کنترل‌کننده [Faces Servlet] ارسال می‌کند. این کنترل‌کننده تمام درخواست‌های مشتری را مدیریت می‌کند. این نقطه ورود به برنامه است. این C در MVC است،

۲ – پردازش – کنترل‌کننده C این درخواست را پردازش می‌کند. برای این کار، از رویدادپردازهای مخصوص برنامه که با نام [2a] نوشته شده‌اند، کمک می‌گیرد. این رویدادپردازها ممکن است به کمک لایه کسب‌وکار، [2b]، نیاز داشته باشند. پس از پردازش درخواست مشتری، ممکن است پاسخ‌های مختلفی ایجاد شود. یک مثال معمول عبارت است از:

  • یک صفحه خطا اگر درخواست نتوانست به درستی پردازش شود؛
  • در غیر این صورت، یک صفحه تأیید،

۳ – ناوبری – کنترل‌کننده پاسخ (= نما) را که باید به کلاینت ارسال شود، انتخاب می‌کند. انتخاب پاسخ برای ارسال به کلاینت شامل چندین مرحله است:

  • انتخاب Facelet که پاسخ را تولید می‌کند. این به عنوان view V شناخته می‌شود، V در MVC. این انتخاب عموماً به نتیجه اجرای عملی که توسط کاربر درخواست شده است بستگی دارد؛
  • تأمین داده‌های مورد نیاز این Facelet برای تولید این پاسخ. در واقع، این پاسخ اغلب شامل اطلاعاتی است که توسط کنترل‌کننده محاسبه شده است. این اطلاعات همان چیزی را تشکیل می‌دهد که به آن مدل M نما (view's model M) گفته می‌شود، M در MVC،

مرحله ۳ بنابراین شامل انتخاب یک نما V و ساخت مدل M مورد نیاز برای آن است.

۴ – پاسخ – کنترل‌کننده C به Facelet انتخاب‌شده دستور می‌دهد تا خود را نمایش دهد. Facelet از مدل M که توسط کنترل‌کننده C آماده شده است برای راه‌اندازی بخش‌های پویا از پاسخی که باید به کلاینت ارسال کند، استفاده می‌کند. قالب دقیق این پاسخ ممکن است متفاوت باشد: می‌تواند یک جریان HTML، یک جریان PDF، یک فایل اکسل و غیره باشد.

در یک پروژه JSF:

  • کنترل‌کننده C، سرولِت [javax.faces.webapp.FacesServlet] است،
  • ویوهایی که با V نشان داده شده‌اند، توسط صفحاتی با استفاده از فناوری Facelets پیاده‌سازی می‌شوند،
  • مدل‌های M و رویدادپردازها توسط کلاس‌های جاوا پیاده‌سازی می‌شوند که اغلب به آن‌ها «بیان‌های پشتیبان» (backing beans) یا به سادگی، Beans گفته می‌شود.

بیایید بار دیگر به محتویات فایل [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>
    <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>
</web-app>
  • خطوط ۱۲–۱۵: تگ <servlet-mapping> برای مرتبط کردن یک servlet با یک URL که توسط مرورگر مشتری درخواست شده است، استفاده می‌شود. در اینجا مشخص شده است که فایل‌های URL با فرمت [/faces/*] باید توسط سرولتی به نام [Faces Servlet] پردازش شوند. این موضوع در خطوط ۷ تا ۱۰ تعریف شده است. از آنجایی که هیچ تگ <servlet-mapping> دیگری در فایل وجود ندارد، این بدان معناست که سرولت [Faces Servlet] تنها درخواست‌های URL با قالب [/faces/*] را پردازش خواهد کرد. ما دیدیم که زمینهٔ برنامه [/mv-jsf2-01] نامیده می‌شود. بنابراین، فایل‌های URL مشتریان که توسط servlet [Faces Servlet] پردازش می‌شوند، شکل [http://machine:port/mv-jsf2-01/faces/*] را خواهند داشت. صفحات .html و .jsp به‌طور پیش‌فرض توسط خود کانتینر سرولت و نه توسط یک سرولت خاص پردازش می‌شوند. این به آن دلیل است که کانتینر سرولت می‌داند چگونه آن‌ها را پردازش کند،
  • خطوط ۷–۱۰: سرولِت [Faces Servlet] را تعریف می‌کنند. از آنجایی که تمام درخواست‌های پذیرفته‌شده URL به آن هدایت می‌شوند، این سرولِت به‌عنوان کنترل‌کننده C برای مدل MVC عمل می‌کند،
  • خط ۱۰: مشخص می‌کند که سرولت باید به محض راه‌اندازی وب‌سرور در حافظه بارگذاری شود. به طور پیش‌فرض، یک سرولت تنها هنگام دریافت اولین درخواست به آن بارگذاری می‌شود،
  • خطوط ۳–۶: یک پارامتر برای servlet [Faces Servlet] تعریف می‌کنند. پارامتر javax.faces.PROJECT_STAGE مرحله‌ای را که پروژه در آن اجرا می‌شود، تعریف می‌کند. در مرحله توسعه، سرولت [Faces Servlet] پیام‌های خطایی را که برای عیب‌یابی مفید هستند، نمایش می‌دهد. در مرحله تولید، این پیام‌ها دیگر نمایش داده نمی‌شوند،
  • خطوط 17–19: مدت‌زمان جلسه به دقیقه. یک مشتری از طریق مجموعه‌ای از چرخه‌های درخواست/پاسخ با برنامه تعامل می‌کند. هر چرخه از اتصال TCP-IP خود استفاده می‌کند که برای هر چرخه از نو ایجاد می‌شود. بنابراین، اگر یک کلاینت C دو درخواست D1 و D2 ارسال کند، سرور S هیچ راهی برای تشخیص اینکه هر دو درخواست متعلق به یک کلاینت C هستند، ندارد. سرور S حافظه کلاینت را در اختیار ندارد. این یک ویژگی پروتکل HTTP مورد استفاده (پروتکل انتقال HyperText) است: کلاینت از طریق مجموعه‌ای از چرخه‌های درخواست کلاینت/پاسخ سرور با سرور ارتباط برقرار می‌کند که در هر یک از آن‌ها از یک اتصال TCP-IP جدید استفاده می‌شود. این به عنوان یک پروتکل بدون حالت شناخته می‌شود. در پروتکل‌های دیگر، مانند FTP (پروتکل انتقال فایل)، کلاینت C از همان اتصال برای تمام مدت تعامل خود با سرور S استفاده می‌کند. بنابراین، یک اتصال به یک کلاینت خاص مرتبط می‌شود. سرور S همیشه می‌داند که با چه کسی در حال تعامل است. وب‌سرور برای تشخیص اینکه یک درخواست متعلق به کلاینت معین است، می‌تواند از تکنیک جلسه (session) استفاده کند:
    • هنگامی که یک کلاینت اولین درخواست خود را ارسال می‌کند، سرور S پاسخ مورد انتظار را به همراه یک توکن، یک رشته تصادفی از کاراکترها که منحصر به آن کلاینت است، برای آن ارسال می‌کند؛
    • در هر درخواست بعدی، کلاینت C توکن دریافت‌شده را به سرور S بازمی‌فرستد و بدین ترتیب به سرور S امکان می‌دهد تا آن را شناسایی کند.

اکنون برنامه می‌تواند از سرور بخواهد تا اطلاعات مرتبط با یک کلاینت خاص را ذخیره کند. این به عنوان جلسه کلاینت شناخته می‌شود. خط ۱۸ مشخص می‌کند که طول عمر یک جلسه ۳۰ دقیقه است. این بدان معناست که اگر کلاینت C در عرض ۳۰ دقیقه درخواست جدیدی ارسال نکند، جلسه او خاتمه یافته و اطلاعات موجود در آن از بین می‌رود. در درخواست بعدی، همه چیز گویی یک کلاینت جدید است و یک جلسه جدید آغاز خواهد شد،

  • خطوط ۲۱–۲۳: فهرست صفحاتی که هنگام درخواست کاربر برای دسترسی به زمینه بدون مشخص کردن صفحه، برای مثال [http://machine:port/mv-jsf2-01]، نمایش داده می‌شوند. در این حالت، سرور وب (نه سرولت) بررسی می‌کند که آیا برنامه برچسب <welcome-file-list> را تعریف کرده است یا خیر. در صورت مثبت بودن پاسخ، صفحه اول یافت‌شده در لیست را نمایش می‌دهد. اگر آن صفحه وجود نداشته باشد، صفحه دوم را نمایش می‌دهد و این کار به همین ترتیب تا زمانی که یک صفحه موجود پیدا شود، ادامه می‌یابد. در اینجا، هنگامی که کلاینت URL [http://machine:port/mv-jsf2-01] را درخواست می‌کند، صفحه URL [http://machine:port/mv-jsf2-01/index.xhtml] به او ارائه می‌شود.

2.3.5. اجرای پروژه

وقتی پروژه جدید اجرا می‌شود، نتیجه نمایش داده شده در مرورگر به شرح زیر است:

  • در [1]، زمینه بدون مشخص کردن یک سند درخواست شده است،
  • در [2]، همانطور که توضیح داده شد، صفحه اصلی (فایل خوشامدگویی) [index.xhtml] ارائه می‌شود.

ممکن است کنجکاو باشید که کد منبع دریافت‌شده را مشاهده کنید، [3]:

1
2
3
4
5
6
7
<?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"><head>
    <title>Facelet Title</title></head><body>
    Hello from Facelets
  </body>
</html>

ما HTML را دریافت کردیم. تمام تگ‌های <h:xx> در index.xhtml به معادل‌هایشان در HTML تبدیل شده‌اند.

2.3.6. انبار محلی Maven

ما اشاره کردیم که Maven وابستگی‌های مورد نیاز پروژه را دانلود کرده و به‌صورت محلی ذخیره می‌کند. شما می‌توانید این مخزن محلی را بررسی کنید:

  • در [1]، گزینه [Window / Other / Maven Repository Browser] را انتخاب کنید،
  • در [2]، یک تب با برچسب [Maven Repositories] باز می‌شود،
  • در [3]، دو شاخه وجود دارد، یکی برای مخزن محلی و دیگری برای مخزن مرکزی. شاخه دوم بسیار بزرگ است. برای مشاهده محتویات آن، باید نمایه آن [4] را به‌روزرسانی کنید. این به‌روزرسانی چندین ده دقیقه طول می‌کشد.
  • در [5]، کتابخانه‌های مخزن محلی،
  • به [6]؛ در داخل آن، یک شاخه [istia.st] وجود دارد که با [groupId] در پروژه ما مطابقت دارد،
  • در [7]، می‌توانید به ویژگی‌های مخزن محلی دسترسی پیدا کنید؛
  • در [8]، مسیر مخزن محلی را خواهید یافت. دانستن این موضوع مفید است زیرا گاهی (به ندرت) Maven دیگر از آخرین نسخه پروژه استفاده نمی‌کند. شما تغییراتی ایجاد می‌کنید و متوجه می‌شوید که این تغییرات منعکس نمی‌شوند. سپس می‌توانید به‌صورت دستی شاخه‌ای را که در مخزن محلی مربوط به [groupId] شماست، حذف کنید. این کار Maven را مجبور می‌کند تا شاخه را از آخرین نسخه پروژه دوباره ایجاد کند.

2.3.7. جستجوی یک آرتیفکت با Maven

حال بیاموزیم چگونه با استفاده از Maven یک آرتیفکت را جستجو کنیم. بیایید با لیست وابستگی‌های فعلی برای فایل [pom.xml] شروع کنیم:


<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-jsf2-01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mv-jsf2-01</name>

  ...
  <dependencies>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.1.1-b04</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.1.1-b04</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    ...
  </build>
  <repositories>
    <repository>
      <url>http://download.java.net/maven/2/</url>
      <id>jsf20</id>
      <layout>default</layout>
      <name>Repository for library Library[jsf20]</name>
    </repository>
    <repository>
      <url>http://repo1.maven.org/maven2/</url>
      <id>jstl11</id>
      <layout>default</layout>
      <name>Repository for library Library[jstl11]</name>
    </repository>
  </repositories>
</project>

خطوط ۱۳ تا ۴۰ وابستگی‌ها را تعریف می‌کنند و خطوط ۴۵ تا ۵۸ مخازنی را که در آن‌ها می‌توان این وابستگی‌ها را یافت مشخص می‌کنند، علاوه بر مخزن مرکزی که همیشه استفاده می‌شود. ما قصد داریم وابستگی‌ها را اصلاح کنیم تا از آخرین نسخه‌های کتابخانه‌ها استفاده کنیم.

ابتدا وابستگی‌های فعلی [1] را حذف می‌کنیم. سپس فایل [pom.xml] اصلاح می‌شود:


<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>
...
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
...
    <repositories>
        <repository>
            <url>http://download.java.net/maven/2/</url>
            <id>jsf20</id>
            <layout>default</layout>
            <name>Repository for library Library[jsf20]</name>
        </repository>
        <repository>
            <url>http://repo1.maven.org/maven2/</url>
            <id>jstl11</id>
            <layout>default</layout>
            <name>Repository for library Library[jstl11]</name>
        </repository>
    </repositories>
</project>

خطوط ۵–۱۲: وابستگی‌های حذف‌شده دیگر در [pom.xml] دیده نمی‌شوند. اکنون بیایید آن‌ها را در مخازن Maven جستجو کنیم.

  • در [1]، یک وابستگی به پروژه اضافه می‌شود؛
  • در [2]، باید جزئیات مربوط به آرتیفکت مورد جستجو (groupId، artifactId، نسخه، بسته‌بندی (نوع) و دامنه) را مشخص کنیم. ما با مشخص کردن [groupId] [3] شروع می‌کنیم،
  • در [4]، ما [espace] را تایپ می‌کنیم تا فهرست آثار احتمالی نمایش داده شود. در اینجا، [jsf-api] و [jsf-impl]. ما [jsf-api] را انتخاب می‌کنیم،
  • و برای [5]، با دنبال کردن همان رویه، جدیدترین نسخه را انتخاب می‌کنیم. نوع بسته‌بندی jar است.

ما این رویه را برای همه مصنوعات دنبال می‌کنیم:

در [6]، وابستگی‌های افزوده شده در پروژه ظاهر می‌شوند. فایل [pom.xml] این تغییرات را منعکس می‌کند:


<dependencies>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1.7</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.1.7</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

حال فرض کنید که ما [groupId] مربوط به محصول مورد نظرمان را نمی‌دانیم. برای مثال، می‌خواهیم از Hibernate به عنوان ORM (نقشه‌بردار شیء-رابطه‌ای) استفاده کنیم و همین تمام چیزی است که می‌دانیم. سپس می‌توانیم به وب‌سایت [http://mvnrepository.com/] مراجعه کنیم:

در [1] می‌توانید کلمات کلیدی را وارد کنید. بیایید hibernate را تایپ کرده و جستجو را اجرا کنیم.

  • در [2]، بیایید [groupIdorg.hibernate و [artifactIdhibernate-core را انتخاب کنیم،
  • از [3]، بیایید نسخه 4.1.2-Final را انتخاب کنیم،
  • برای [4]، کد Maven را برای چسباندن در فایل [pom.xml] به‌دست می‌آوریم. این کار را انجام می‌دهیم.

<dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.1.2.Final</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.1.7</version>
      <type>jar</type>
    </dependency>
    ...
  </dependencies>

ما فایل [pom.xml] را ذخیره می‌کنیم. سپس Maven دانلود وابستگی‌های جدید را آغاز می‌کند. پروژه به شرح زیر توسعه می‌یابد:

  • تا [5]، وابستگی [hibernate-core-4.1.2-Final]. در مخزنی که در آن یافت شده است، این [artifactId] همچنین توسط فایلی به نام [pom.xml] توصیف می‌شود. این فایل خوانده شد و Maven متوجه شد که [artifactId] وابستگی‌هایی دارد. این وابستگی‌ها نیز دانلود می‌شوند. این کار برای هر [artifactId] دانلود شده انجام خواهد شد. در نهایت، فایل [6] شامل وابستگی‌هایی است که ما مستقیماً درخواست نکرده‌ایم. این موارد با آیکونی نشان داده می‌شوند که با آیکون فایل اصلی [artifactId] متفاوت است.

در این سند، ما عمدتاً از Maven برای این ویژگی استفاده می‌کنیم. این کار ما را از دانستن تمام وابستگی‌های کتابخانه‌ای که می‌خواهیم استفاده کنیم، بی‌نیاز می‌سازد. ما اجازه می‌دهیم Maven آن‌ها را مدیریت کند. علاوه بر این، با به اشتراک گذاشتن یک فایل [pom.xml] در میان توسعه‌دهندگان، می‌توانیم مطمئن شویم که هر توسعه‌دهنده از همان کتابخانه‌ها استفاده می‌کند.

در مثال‌های بعدی، ما به سادگی فایل [pom.xml] مورد استفاده را ارائه خواهیم داد. خواننده تنها با استفاده از این فایل می‌تواند شرایط توصیف‌شده در این سند را بازتولید کند. علاوه بر این، پروژه‌های Maven توسط اصلی‌ترین محیط‌های توسعه یکپارچه (IDE) جاوا (Eclipse, NetBeans, IntelliJ, JDeveloper) پشتیبانی می‌شوند. بنابراین خواننده می‌تواند از IDE مورد علاقه خود برای آزمایش مثال‌ها استفاده کند.

2.4. مثال mv-jsf2-02: رویدادپرداز – بین‌المللی‌سازی – پیمایش صفحه

2.4.1. برنامه

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

  • در [1]، صفحه اصلی برنامه،
  • در [2]، دو پیوند برای تغییر زبان صفحات برنامه،
  • به [3]، یک لینک ناوبری به صفحه دیگر،
  • وقتی روی [3] کلیک می‌کنید، صفحه [4] نمایش داده می‌شود،
  • لینک [5] شما را به صفحه اصلی بازمی‌گرداند.
  • در صفحه اصلی [1]، پیوندهای [2] به شما امکان تغییر زبان را می‌دهند،
  • روی [3]، صفحهٔ اصلی انگلیسی.

2.4.2. پروژه NetBeans

یک پروژه وب جدید همان‌طور که در بخش 2.3.1 توضیح داده شده است، ایجاد خواهد شد. نام آن mv-jsf2-02 خواهد بود:

  • در [1]، پروژهٔ تولیدشده،
  • در [2]، ما پکیج [istia.st.mvjsf202] و فایل [index.jsp] را حذف کرده‌ایم،
  • در [3]، وابستگی‌های Maven با استفاده از فایل زیر، [pom.xml]، اضافه شدند:

<dependencies>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

وابستگی‌های افزوده شده مربوط به چارچوب JSF هستند. به سادگی خطوط بالا را در فایل [pom.xml] کپی کرده و جایگزین وابستگی‌های قدیمی کنید.

  • در [4, 5]: یک پوشه با نام [src / main / resources] را در داخل برگه [Files] ایجاد کنید،
  • در [6]، در داخل زبانه [Projects]، این شاخه [Other Sources] ایجاد شد.

اکنون یک پروژه به نام JSF داریم. ما انواع مختلفی از فایل‌ها را در آن ایجاد خواهیم کرد:

  • صفحات وب با فرمت XHTML،
  • کلاس‌های جاوا،
  • فایل‌های پیام،
  • فایل پیکربندی پروژه JSF.

بیایید ببینیم چگونه هر نوع فایل را ایجاد کنیم:

  • در [1]، ما یک صفحه JSF ایجاد می‌کنیم
  • از [2]، ما یک صفحه [index.xhtml] را در قالب [Facelets] [3]، ایجاد می‌کنیم،
  • در [4]، دو فایل ایجاد شده‌اند: [index.xhtml] و [WEB-INF / web.xml].

فایل [web.xml] برنامه JSF را پیکربندی می‌کند. این فایل به شرح زیر است:


<?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>
    <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>
</web-app>

ما قبلاً این فایل را در بخش 2.3.4 مورد بحث قرار داده‌ایم. بیایید ویژگی‌های اصلی آن را مرور کنیم:

  • تمام فایل‌های URL از نوع faces/* توسط سرولت [javax.faces.webapp.FacesServlet] پردازش می‌شوند،
  • صفحه [index.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">
  <h:head>
    <title>Facelet Title</title>
  </h:head>
  <h:body>
    Hello from Facelets
  </h:body>
</html>

ما قبلاً این فایل را در بخش 2.3.4 دیده‌ایم.

حال بیایید یک کلاس جاوا بسازیم:

  • در [1]، یک کلاس جاوا در شاخه [Source Packages] ایجاد می‌کنیم،
  • در [2]، به آن نام می‌دهیم و آن را در پکیج [3] قرار می‌دهیم،
  • در [4]، کلاس در پروژه ظاهر می‌شود.

کد کلاس ایجادشده، یک اسکلت کلاس است:


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package istia.st;

/**
 *
 * @author Serge Tahé
 */
public class Form {
  
}

در نهایت، بیایید یک فایل پیام ایجاد کنیم:

  • در [1]، فایلی با نام [Properties] ایجاد کنید،
  • در [2]، نام فایل و در [3]، پوشه آن را مشخص می‌کنیم،
  • در [4]، فایل [messages.properties] ایجاد شده است.

گاهی لازم است فایل [WEB-INF/faces-config.xml] را برای پیکربندی پروژه JSF ایجاد کنید. این فایل با JSF 1 الزامی بود. با JSF 2 اختیاری است. با این حال، اگر سایت JSF بین‌المللی‌سازی شود، این فایل مورد نیاز است. این وضعیت در ادامه پیش خواهد آمد. بنابراین اکنون به شما نشان خواهیم داد چگونه این فایل پیکربندی را ایجاد کنید.

  • در [1]، فایل پیکربندی JSF را ایجاد می‌کنیم،
  • در [2]، نام آن را مشخص می‌کنیم و در [3]، پوشه آن را،
  • در [4]، فایل ایجاد شده است.

فایل [faces-config.xml] ایجادشده به شرح زیر است:


<?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">


</faces-config>

تگ ریشه <faces-config> است. محتوای این تگ خالی است. باید آن را پر کنیم.

اکنون تمام عناصر لازم برای ایجاد یک پروژه JSF را داریم. در مثال‌های بعدی، پروژه کامل JSF را ارائه کرده و سپس به صورت موردی عناصر آن را بررسی می‌کنیم. اکنون برای توضیح مفاهیم، یک پروژه ارائه می‌کنیم:

  • پردازش رویداد فرم،
  • بین‌المللی‌سازی صفحات در یک وب‌سایت JSF،
  • ناوبری بین صفحات.

پروژه [mv-jsf2-02] به شرح زیر است. خوانندگان می‌توانند آن را در وب‌سایت مثال‌ها (به بخش 1.2 مراجعه کنید) بیابند.

  • در [1]، فایل‌های پیکربندی برای پروژه JSF،
  • در [2]، صفحات JSF پروژه،
  • به [3]، تک‌کلاس جاوا،
  • به [4]، فایل‌های پیام.

2.4.3. صفحه [index.xhtml]

فایل [index.xhtml] [1] صفحه [2] را به مرورگر کلاینت ارسال می‌کند:

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


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      ...
    </head>
    <body>
      ....
    </body>
  </f:view>
</html>
  • خطوط ۷–۹: فضاهای نام / کتابخانه‌های تگ مورد استفاده توسط صفحه. تگ‌هایی که با 'h' شروع می‌شوند، تگ‌های HTML هستند، در حالی که تگ‌هایی که با 'f' شروع می‌شوند، تگ‌های مخصوص JSF هستند،
  • خط ۱۰: تگ <f:view> برای جدا کردن کدی که باید توسط موتور JSF پردازش شود، یعنی بخشی که حاوی تگ‌های <f:xx> است، استفاده می‌شود. ویژگی locale برای مشخص کردن زبان نمایش صفحه به کار می‌رود. در اینجا از دو زبان استفاده خواهیم کرد: انگلیسی و فرانسوی. مقدار ویژگی `locale` به صورت یک عبارت EL (زبان بیان) `#{expression}` بیان می‌شود. شکل عبارت می‌تواند متفاوت باشد. ما اغلب آن را به شکل `bean['clé']` یا `bean.champ` بیان خواهیم کرد. در مثال‌های ما، `bean` یا یک کلاس جاوا خواهد بود یا یک فایل پیام. در JSF 1، این بی́n‌ها باید در فایل [faces-config.xml] اعلام می‌شدند. در JSF 2، این کار برای کلاس‌های جاوا دیگر الزامی نیست. اکنون می‌توان از anotationها استفاده کرد که یک کلاس جاوا را به‌عنوان یک bean شناخته‌شده توسط JSF 2 مشخص می‌کنند. فایل پیام باید در فایل پیکربندی [faces-config.xml] اعلام شود.

2.4.4. بیان [changeLocale]

در عبارت EL #{changeLocale.locale}:

  • changeLocale نام یک بین است، در این مورد کلاس جاوا ChangeLocale،
  • locale یک فیلد از کلاس ChangeLocale است. این عبارت به صورت [ChangeLocale].getLocale() ارزیابی می‌شود. به طور کلی، عبارت #{bean.champ} به صورت [Bean] ارزیابی می‌شود.getChamp()، که در آن [Bean] نمونه‌ای از کلاس جاوایی به نام bean و getChamp است، گیرندهٔ مرتبط با فیلد champ دانه.

کلاس ChangeLocale به شرح زیر است:


package utils;

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.enterprise.context.SessionScoped;

@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
  // محلی که صفحات در آن قرار دارند
  private String locale="fr";
  
  public ChangeLocale() {
  }
  
  ...
  public String getLocale() {
    return locale;
  }
  
}
  • خط ۱۱: فیلد `local
  • خط 17: گتر آن،
  • خط ۷: anotation ManagedBean کلاس جاوا ChangeLocale را به یک bean شناخته‌شده توسط JSF تبدیل می‌کند. یک بین با یک نام شناسایی می‌شود. این نام را می‌توان با استفاده از ویژگی name anotation مشخص کرد: @ManagedBean(name="xx"). اگر ویژگی name وجود نداشته باشد، نام کلاس با حرف اول کوچک استفاده می‌شود. بنابراین نام bean ChangeLocale، changeLocale است. توجه داشته باشید که anotation ManagedBean متعلق به package javax.faces.bean.ManagedBean است و نه به package javax.annotations.ManagedBean.
  • خط ۸: تگ SessionScoped دامنهٔ عمر بین را تعریف می‌کند. چندین تگ از این نوع وجود دارد. ما معمولاً از سه مورد زیر استفاده خواهیم کرد:
    • RequestScoped: عمر این بیون با چرخه درخواست مرورگر/پاسخ سرور مطابقت دارد. اگر این بیون دوباره برای پردازش یک درخواست جدید از همان مرورگر یا مرورگری دیگر مورد نیاز باشد، مجدداً نمونه آن ایجاد خواهد شد،
    • SessionScoped: عمر این بیون با عمر جلسه (session) یک مشتری خاص مطابقت دارد. این بیون در ابتدا برای پردازش یکی از درخواست‌های آن مشتری ایجاد می‌شود. سپس در حافظه همان جلسه مشتری باقی می‌ماند. چنین بیونی معمولاً داده‌های مخصوص یک مشتری معین را ذخیره می‌کند. این بیون هنگام پایان جلسه مشتری از بین می‌رود،
    • ApplicationScoped: عمر این بیون با عمر خود برنامه مطابقت دارد. بیونی با این نوع عمر اغلب توسط همه مشتریان برنامه به اشتراک گذاشته می‌شود. این بیون معمولاً در شروع برنامه اولیه می‌شود.

این anotationها در دو پکیج یافت می‌شوند: javax.enterprise.context.SessionScoped (JSF 2) و javax.faces.bean.SessionScoped (JSF 1). در اینجا، ما از بسته JSF 2 استفاده می‌کنیم. این مستلزم ایجاد فایل [WEB-INF / beans.xml] است:

  

این فایل به‌طور خودکار توسط NetBeans هنگام وارد کردن بسته [javax.enterprise.context.SessionScoped] ایجاد می‌شود. محتویات آن به شرح زیر است:


<?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>

به جز تگ ریشه <beans>، این فایل خالی است. این کافی است. تنها وجود آن لازم است.

در نهایت باید توجه داشت که کلاس [ChangeLocale] رابط [Serializable] را پیاده‌سازی می‌کند. این امر برای بی́n‌هایی با دامنه Session الزامی است، که وب‌سرور ممکن است نیاز داشته باشد آن‌ها را به فایل‌ها سریالیزه کند. ما در مرحله بعد به بین [ChangeLocale] باز خواهیم گشت.

2.4.5. فایل پیام

بیایید به فایل [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:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
    ...
    </body>
  </f:view>
</html>
  • خط ۸: تگ <h:outputText> مقدار یک عبارت EL به شکل #{msg['welcome.titre']} که از نوع #{bean['champ']} است را نمایش می‌دهد. 'bean' یا نام یک کلاس جاوا است یا نام یک فایل پیام. در اینجا، نام یک فایل پیام است. این باید در فایل پیکربندی [faces-config.xml] اعلام شود. bean 'msg' به صورت زیر اعلام می‌شود:

<?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>
  </application>
</faces-config>
  • خطوط ۱۱–۱۸: تگ <application> برای پیکربندی برنامه JSF استفاده می‌شود،
  • خطوط ۱۲–۱۷: تگ <resource-bundle> برای تعریف منابع برای برنامه استفاده می‌شود، در این مورد یک فایل پیام،
  • خطوط ۱۳–۱۵: تگ <base-name> نام فایل پیام را تعریف می‌کند،
  • خط ۱۴: این فایل با نام messages[_CodeLangue][_CodePays].properties فراخوانی خواهد شد. تگ <base-name> تنها بخش اول نام را تعریف می‌کند. بقیه به‌طور ضمنی مشخص می‌شود. ممکن است چندین فایل پیام وجود داشته باشد، یکی برای هر زبان:
  • در [1]، چهار فایل پیام وجود دارد که متناظر با نام پیام پایه تعریف‌شده در [faces-config.xml] هستند،
    • messages_fr.properties: حاوی پیام‌ها به زبان فرانسوی (کد fr) است؛
    • messages_en.properties: حاوی پیام‌ها به زبان انگلیسی (کد en) است؛
    • messages_es_ES.properties: حاوی پیام‌هایی به زبان اسپانیایی (کد es) از اسپانیا (کد ES) است. گونه‌های دیگری از اسپانیایی نیز وجود دارد، مانند اسپانیایی بولیوی (es_BO);
    • messages.properties: توسط سرور زمانی استفاده می‌شود که زبان سیستمی که روی آن اجرا می‌شود، فايل پيام همراه‌شده‌ای نداشته باشد. برای مثال، اگر برنامه کاربردی روی سیستمی در آلمان اجرا شود که زبان پیش‌فرض آن آلمانی (de) است، از این فایل استفاده خواهد شد. از آنجایی که فایل [messages_de.properties] وجود ندارد، برنامه از فایل [messages.properties] استفاده می‌کند،
  • در [2]: کدهای زبان تابع یک استاندارد بین‌المللی هستند،
  • در [3]: همین امر در مورد کدهای کشور نیز صدق می‌کند.

نام فایل پیام در خط 14 تعریف می‌شود. این فایل در پوشه Classpath پروژه جستجو خواهد شد. اگر در یک بسته قرار داشته باشد، این موضوع باید در خط 14 مشخص شود، برای مثال ressources.messages، اگر فایل [messages.properties] در پوشه [ressources] از Classpath قرار داشته باشد. از آنجا که نام در خط 14 شامل بسته نیست، فایل [messages.properties] باید در ریشه پوشه [src / main / resources] قرار گیرد:

در [1]، در برگه [Projects] پروژه NetBeans، فایل [messages.properties] به صورت فهرستی از نسخه‌های مختلف پیام‌های تعریف‌شده نمایش داده می‌شود. نسخه‌ها با دنباله‌ای از یک تا سه کد [codeLangue_codePays_codeVariante] شناسایی می‌شوند. در [1]، تنها از کد [codeLangue] استفاده شده است: «en» برای انگلیسی و «fr» برای فرانسوی. هر نسخه در یک فایل جداگانه در سیستم فایل ذخیره می‌شود.

در مثال ما، فایل پیام فرانسوی [messages_fr.properties] شامل عناصر زیر خواهد بود:


welcome.titre=Tutoriel JSF (JavaServer Faces)
welcome.langue1=Fran\u00e7ais
welcome.langue2=Anglais
welcome.page1=Page 1
page1.titre=page1
page1.entete=Page 1
page1.welcome=Page d'accueil

از سوی دیگر، فایل [messages_en.properties] به شرح زیر خواهد بود:


welcome.titre=JSF (JavaServer Faces) Tutorial
welcome.langue1=French
welcome.langue2=English
welcome.page1=Page 1
page1.titre=page1
page1.entete=Page 1
page1.welcome=Welcome page

فایل [messages.properties] با فایل [messages_en.properties] یکسان است. در نهایت، مرورگر کلاینت قادر خواهد بود بین صفحات فرانسوی و صفحات انگلیسی انتخاب کند.

بیایید به فایل [faces-config.xml] بازگردیم که فایل پیام را اعلام می‌کند:


...

  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
  </application>
</faces-config>

خط ۸ نشان می‌دهد که یک خط از فایل پیام توسط شناسه‌ی 'msg' در صفحات JSF ارجاع داده خواهد شد. این شناسه‌ی در فایل [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:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
      ...
    </body>
  </f:view>
</html>

تگ <h:outputText> در خط ۸، مقدار پیام (جایی که شناسه 'msg' وجود دارد) را با کلید welcome.titre نمایش می‌دهد. این پیام در فایل [messages.properties] برای زبان فعال فعلی جستجو و یافت می‌شود. برای مثال، برای زبان فرانسوی:


welcome.titre=Tutoriel JSF (JavaServer Faces)

یک پیام به شکل کلید=مقدار است. خط ۸ فایل [index.xhtml] پس از ارزیابی عبارت #{msg['welcome.titre']} به شکل زیر درمی‌آید:


      <title><h:outputText value="Tutoriel JSF (JavaServer Faces)" /></title>

این مکانیزم فایل پیام تغییر زبان صفحات در پروژه JSF را آسان می‌کند. به این فرایند بین‌المللی‌سازی پروژه گفته می‌شود، یا معمولاً با مخفف i18n، زیرا واژه «internationalisation» با «i» شروع و با «n» پایان می‌یابد و بین «i» و «n» ۱۸ حرف قرار دارد.

2.4.6. فرم

بیایید به بررسی محتوای فایل [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:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
      </h:form>
    </body>
  </f:view>
</html>
  • خطوط ۱۱–۱۸: تگ <h:form> یک فرم را معرفی می‌کند. یک فرم عموماً شامل موارد زیر است:
    • برچسب‌های فیلد ورودی (متن، دکمه‌های رادیویی، چک‌باکس‌ها، لیست‌های کشویی و غیره)؛
    • برچسب‌های اعتبارسنجی فرم (دکمه‌ها، پیوندها). کاربر از طریق یک دکمه یا پیوند ورودی‌های خود را به سرور ارسال می‌کند، که سرور آن‌ها را پردازش خواهد کرد،

هر تگ JSF را می‌توان با یک ویژگی id شناسایی کرد. در اکثر موارد، استفاده از این ویژگی ضروری نیست و برای اکثریت تگ‌های JSF مورد استفاده در اینجا نیز همین کار انجام شده است. با این حال، این ویژگی در شرایط خاصی مفید است. خط 17: فرم با شناسه «form» شناسایی می‌شود. در این مثال، شناسه فرم مورد استفاده قرار نمی‌گیرد و می‌توانست حذف شود.

  • خطوط ۱۸–۲۱: تگ <h:panelGrid> در اینجا یک جدول دو ستونی را تعریف می‌کند. این تگ، تگ را تولید می‌کند،
  • فرم دارای سه پیوند است که در خطوط ۱۹، ۲۰ و ۲۳ پردازش آن را آغاز می‌کنند. تگ <h:commandLink> حداقل دو ویژگی دارد:
    • value: متن لینک؛
    • action: یا یک رشتهٔ کاراکتری C، یا ارجاع به متدی که هنگام اجرا، آن رشتهٔ کاراکتری C را بازمی‌گرداند. این رشتهٔ کاراکتری C می‌تواند:
      • یا نام یک صفحه JSF در پروژه،
      • یا نامی که در قوانین ناوبری فایل [faces-config.xml] تعریف شده و با یک صفحه JSF در پروژه مرتبط است؛

در هر دو حالت، صفحه JSF پس از اجرای عملی که توسط ویژگی action تعریف شده است، نمایش داده می‌شود.

بیایید نحوه پردازش فرم‌ها را با استفاده از لینک در خط ۱۳ به‌عنوان مثال بررسی کنیم:


         <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>}"/>

ابتدا، فایل پیام برای جایگزینی عبارت #{msg['welcome.langue1']} با مقدار آن استفاده می‌شود. پس از ارزیابی، تگ به شکل زیر درمی‌آید:


<h:commandLink value="Français" action="#{changeLocale.setFrenchLocale}"/>}"/>

ترجمه این تگ JSF به صورت زیر خواهد بود:

<a href="<a href="view-source:http://localhost:8080/mv-jsf2-02/faces/page1.xhtml#">#</a>" onclick="mojarra.jsfcljs(document.getElementById('formulaire'),{'formulaire:j_idt8':'formulaire:j_idt8'},'');return false">Français</a>

که به ظاهر بصری زیر منجر می‌شود:

به ویژگی onclick تگ در HTML توجه کنید. هنگامی که کاربر روی لینک [Français] کلیک می‌کند، کد جاوااسکریپت اجرا خواهد شد. این کد در صفحه‌ای که توسط مرورگر دریافت می‌شود، جاسازی شده است و خود مرورگر آن را اجرا می‌کند. جاوااسکریپت به طور گسترده در فناوری‌های AJAX (جاوااسکریپت و XML ناهمزمان) استفاده می‌شود. هدف اصلی آن به طور کلی بهبود قابلیت استفاده و پاسخ‌دهی برنامه‌های وب است. این کد اغلب به طور خودکار توسط ابزارهای نرم‌افزاری تولید می‌شود، که در این صورت نیازی به درک آن نیست. با این حال، یک توسعه‌دهنده گاهی ممکن است نیاز داشته باشد که کد جاوااسکریپت را به صفحات JSF خود اضافه کند. در چنین مواردی، دانش جاوااسکریپت ضروری است.

در اینجا نیازی به درک کد جاوااسکریپت تولیدشده برای تگ <h:commandLink> نیست. با این حال، دو نکته قابل توجه است:

  • کد جاوااسکریپت از شناسه‌ی فرم که ما به تگ <h:form> اختصاص دادیم، استفاده می‌کند،
  • JSF برای تمام تگ‌هایی که ویژگی id برایشان تعریف نشده است، شناسه‌های خودکار تولید می‌کند. نمونه‌ای از این مورد را می‌توان در اینجا مشاهده کرد: j_idt8. اعطای شناسه‌های واضح به تگ‌ها، درک کد جاوااسکریپت تولیدشده را در صورت لزوم آسان‌تر می‌کند. این موضوع به‌ویژه زمانی صادق است که توسعه‌دهنده نیاز داشته باشد کد جاوااسکریپت خود را برای دستکاری اجزای صفحه اضافه کند. در چنین مواردی، او باید شناسه‌های «id» این اجزا را بداند.

وقتی کاربر روی لینک [Français] در صفحه بالا کلیک می‌کند چه اتفاقی می‌افتد؟ بیایید معماری یک برنامه JSF را در نظر بگیریم:

کنترل‌کننده [Faces Servlet] درخواست را از مرورگر مشتری به شکل زیر دریافت خواهد کرد: HTTP:

1
2
3
4
5
6
POST /mv-jsf2-02/faces/index.xhtml HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-URLencoded
Content-Length: 126

formulaire=formulaire&javax.faces.ViewState=-9139703055324497810%3A8197824608762605653&formulaire%3Aj_idt8=formulaire%3Aj_idt8 
  • خطوط ۱–۲: مرورگر URL [http://localhost:8080/mv-jsf2-02/faces/index.xhtml] را درخواست می‌کند. این امر همیشه صادق است: داده‌های وارد شده در یک فرم JSF، که در ابتدا از طریق URL و URLFormulaire دریافت شده‌اند، به همین URL ارسال می‌شوند. مرورگر دو روش برای ارسال مقادیر واردشده دارد: GET و POST. با استفاده از روش GET، مقادیر واردشده توسط مرورگر در URL درخواست‌شده ارسال می‌شوند. در مثال بالا، مرورگر می‌توانست خط اول زیر را ارسال کند:

GET /mv-jsf2-02/faces/index.xhtml?formulaire=formulaire&javax.faces.ViewState=-9139703055324497810%3A8197824608762605653&formulaire%3Aj_idt8=formulaire%3Aj_idt8 HTTP/1.1

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

  • خط ۳: فرمت رمزگذاری برای مقادیر فرم را مشخص می‌کند،
  • خط ۴: اندازه خط ۶ را به بایت مشخص می‌کند،
  • خط ۵: یک خط خالی که پایان سربرگ‌های HTTP و شروع ۱۲۶ بایت مقادیر فرم را نشان می‌دهد،
  • خط ۶: مقادیر فرم در قالب element1=value1&element2=value2& ...، با استفاده از فرمت رمزگذاری تعریف‌شده در خط ۳. در این فرمت رمزگذاری، برخی کاراکترها با مقادیر هگزادسیمال خود جایگزین می‌شوند. این مورد در عنصر آخر نیز صادق است:

formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_idt8=formulaire%3Aj_idt8

که در آن %3A نمایانگر کاراکتر : است. بنابراین، رشته form:j_idt8=form:j_idt8 به سرور ارسال می‌شود. شاید به یاد داشته باشید که ما قبلاً با شناسه‌ی j_idt8 زمانی که کد HTML تولیدشده برای تگ را بررسی کردیم، مواجه شده‌ایم


          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>

این شناسه به‌طور خودکار توسط JSF تولید شده بود. نکته مهم این است که وجود این شناسه در رشته مقادیری که توسط مرورگر مشتری ارسال می‌شود، به JSF اجازه می‌دهد تا بداند که لینک [Français] کلیک شده است. سپس از ویژگی action="#{changeLocale.setFrenchLocale}" که در بالا ذکر شد استفاده می‌کند تا تصمیم بگیرد چگونه رشته دریافتی را پردازش کند. ویژگی action="#{changeLocale.setFrenchLocale}" به JSF می‌گوید که درخواست کلاینت باید توسط متد [setFrenchLocale] از ابجکتی به نام changeLocale پردازش شود. همان‌طور که به یاد داریم، این بین با anotationها در کلاس جاوا [ChangeLocale] تعریف شده است:


@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{

نام یک bean توسط ویژگی name در annotation @ManagedBean تعریف می‌شود. اگر این ویژگی وجود نداشته باشد، نام کلاس به عنوان نام bean استفاده می‌شود و اولین حرف آن به صورت کوچک‌نویسی درمی‌آید.

بیایید به درخواست مرورگر بازگردیم:

و به تگ <h:commandLink> که لینک [Français] را تولید کرد و کلیک شد:


          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>

کنترل‌کننده درخواست مرورگر را به دست‌پردازنده رویدادی که توسط ویژگی action تگ <h:commandLink> تعریف شده است، ارسال می‌کند. دست‌پردازنده رویدادی M که توسط ویژگی action تگ <h:commandLink> ارجاع شده است، باید امضای زیر را داشته باشد:

public String M();
  • این هیچ پارامتری دریافت نمی‌کند. خواهیم دید که با این حال می‌تواند به درخواست کلاینت دسترسی داشته باشد؛
  • باید یک نتیجه C از نوع String را بازگرداند. این رشتهٔ کاراکتری C می‌تواند:
    • یا نام یک صفحه JSF در داخل پروژه؛
    • یا نامی که در قوانین ناوبری فایل [faces-config.xml] تعریف شده و با صفحه‌ای JSF در پروژه مرتبط است؛
    • یا یک نشانگر باطله، اگر مرورگر مشتری قرار نیست صفحات را تغییر دهد،

در معماری JSF فوق، کنترل‌کننده [Faces Servlet] از رشته C بازگردانده‌شده توسط دستگیرکننده رویداد و، در صورت لزوم، فایل پیکربندی آن [faces-config.xml] برای تعیین اینکه کدام صفحه JSF باید در پاسخ به کلاینت [4] ارسال کند.

در تگ


          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>

پردازشگر رویداد برای کلیک روی لینک [Français]، متد [changeLocale.setFrenchLocale] است، که در آن changeLocale یک نمونه از کلاس [utils.ChangeLocale] است که قبلاً در مورد آن بحث کرده‌ایم:


package utils;

import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
  // محلی صفحه
  private String locale="fr";
  
  public ChangeLocale() {
  }
  
  public String setFrenchLocale(){
    locale="fr";
    return null;
  }
  
  public String setEnglishLocale(){
    locale="en";
    return null;
  }

  public String getLocale() {
    return locale;
  }
}

متد setFrenchLocale در واقع امضای یک دست‌پردازنده رویداد را دارد. بیایید به یاد بیاوریم که دست‌پردازنده رویداد باید درخواست مشتری را پردازش کند. از آنجا که هیچ پارامتری دریافت نمی‌کند، چگونه می‌تواند به این درخواست دسترسی پیدا کند؟ راه‌های مختلفی برای انجام این کار وجود دارد:

  • بیان B، که شامل رویدادپرداز صفحه JSF P است، اغلب همان بیانی است که مدل M آن صفحه را در خود دارد. این بدان معناست که بین B شامل فیلدهایی است که با مقادیر واردشده در صفحه P مقداردهی اولیه می‌شوند. این کار توسط کنترلر [Faces Servlet] قبل از فراخوانی رویدادپرداز بین B انجام می‌شود. بنابراین رویدادپرداز، از طریق فیلدهای بین B که به آن تعلق دارد، به مقادیر واردشده توسط مشتری در فرم دسترسی خواهد داشت و قادر به پردازش آن‌ها خواهد بود.
  • متد استاتیک [FacesContext.getCurrentInstance()] از نوع [FacesContext] دسترسی به زمینه اجرای درخواست جاری JSF را فراهم می‌کند که یک شیء از نوع [FacesContext] است. زمینه اجرای پرس‌وجو که به این ترتیب به دست می‌آید، دسترسی به پارامترهایی را که توسط مرورگر مشتری به سرور ارسال شده‌اند، از طریق روش زیر فراهم می‌کند:
Map FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()

اگر پارامترهای ارسال‌شده (POST) توسط مرورگر کلاینت به شرح زیر باشند:

formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_id_id21=formulaire%3Aj_id_id21

متد getRequestParameterMap() دیکشنری زیر را بازمی‌گرداند:

کلید
value
فرم
فرم
javax.faces.ViewState
...
form:j_id_id21
form:j_id_id21

در تگ


          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>

از رویدادپرداز locale.setFrenchLocale چه انتظاری می‌رود؟ ما می‌خواهیم که زبان مورد استفادهٔ برنامه را تعیین کند. در اصطلاحات جاوا، این کار «محلی‌سازی» برنامه نامیده می‌شود. این محلی‌سازی توسط تگ <f:view> در صفحه JSF [index.xhtml] استفاده می‌شود:


  <f:view locale="#{changeLocale.locale}">
    ...
</f:view>

برای تغییر صفحه به زبان فرانسوی، کافی است ویژگی locale را روی fr تنظیم کنید. برای تغییر آن به انگلیسی، آن را روی en تنظیم کنید. مقدار ویژگی **locale** از طریق عبارت [ChangeLocale].getLocale() به دست می‌آید. این عبارت مقدار فیلد locale در کلاس [ChangeLocale] را برمی‌گرداند. از این طریق، می‌توانیم کد متد [ChangeLocale].setFrenchLocale() را که مسئول نمایش صفحات به زبان فرانسوی است، استخراج کنیم:


  public String setFrenchLocale(){
    locale="fr";
    return null;
}

ما توضیح داده‌ایم که یک رویدادپرداز باید یک رشته کاراکتری C را بازگرداند که توسط [Faces Servlet] برای یافتن صفحه JSF که باید در پاسخ به مرورگر مشتری ارسال شود، استفاده خواهد شد. اگر صفحه‌ای که باید بازگردانده شود با صفحه‌ای که در حال پردازش است یکسان باشد، دست‌پردازنده رویداد می‌تواند به سادگی مقدار null را بازگرداند. این کاری است که در خط ۳ انجام شده است: ما می‌خواهیم همان صفحه، [index.xhtml]، اما به زبان دیگری را بازگردانیم.

بیایید به معماری پردازش درخواست بازگردیم:

رویدادپرداز changeLocale.setFrenchLocale اجرا شده و مقدار null را به کنترلر [Faces Servlet] بازگردانده است. بنابراین کنترلر صفحه [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:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
      </h:form>
    </body>
  </f:view>
</html>

هرگاه مقداری از نوع #{msg['...']} ارزیابی شود، یکی از فایل‌های پیام [messages.properties] استفاده می‌شود. فایل مورد استفاده، فایلی است که با «محلی‌سازی» صفحه (خط 6) مطابقت دارد. از آنجا که رویدادپرداز changeLocale.setFrenchLocale این بومی‌سازی را روی fr تنظیم می‌کند، فایل [messages_fr.properties] استفاده خواهد شد. کلیک روی لینک [Anglais] (خط 14) محل را به en تغییر می‌دهد (به روش changeLocale.setEnglishLocale مراجعه کنید). سپس از فایل [messages_en.properties] استفاده خواهد شد و صفحه به زبان انگلیسی نمایش داده می‌شود:

هر بار که صفحه [index.xhtml] نمایش داده می‌شود، تگ <f:view> اجرا می‌شود:


  <f:view locale="#{changeLocale.locale}">

و بنابراین متد [ChangeLocale].getLocale() مجدداً اجرا می‌شود. از آنجا که دامنهٔ بین (bean) خود را روی 'Session' تنظیم کرده‌ایم:


@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{

محلی‌سازی انجام‌شده در طول یک درخواست برای درخواست‌های بعدی حفظ می‌شود.

یک عنصر نهایی در صفحه [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:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
      </h:form>
    </body>
  </f:view>
</html>

تگ <h:commandLink> در خط 17 دارای ویژگی 'action' با مقدار یک رشته است. در این مورد، هیچ رویدادپردازشی برای پردازش صفحه فراخوانده نمی‌شود. ما مستقیماً به صفحه [page1.xhtml] هدایت می‌شویم. بیایید بررسی کنیم که در این مورد استفاده، برنامه چگونه کار می‌کند:

کاربر روی لینک [Page 1] کلیک می‌کند. فرم به کنترلر [Faces Servlet] ارسال می‌شود. کنترلر از روی درخواستی که دریافت می‌کند تشخیص می‌دهد که لینک [Page 1] کلیک شده است. آن تگ مربوطه را بررسی می‌کند:


        <h:commandLink value="#{msg['welcome.page1']}" action="page1"/>

هیچ رویدادپرداز (event handler) مرتبط با این لینک وجود ندارد. کنترل‌کننده [Faces Servlet] بلافاصله به مرحله [3] در بالا می‌رود و صفحه [page1.xhtml] را نمایش می‌دهد:

2.4.7. صفحه JSF [page1.xhtml]

صفحه [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:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['page1.titre']}"/></title>
    </head>
    <body>
      <h1><h:outputText value="#{msg['page1.entete']}"/></h1>
      <h:form>
        <h:commandLink value="#{msg['page1.welcome']}" action="index"/>
      </h:form>
    </body>
  </f:view>
</html>

در این صفحه چیزی نیست که قبلاً توضیح داده نشده باشد. خواننده قادر خواهد بود کد JSF را با صفحه‌ای که به مرورگر مشتری ارسال شده است مطابقت دهد. لینک بازگشت به صفحه اصلی:


        <h:commandLink value="#{msg['page1.welcome']}" action="index"/>

صفحه [index.xhtml] نمایش داده خواهد شد.

2.4.8. اجرای پروژه

پروژه ما اکنون کامل است. ما می‌توانیم آن را بسازیم (پاکسازی و ساخت):

  • ساخت پروژه، پوشه [target] را در زبانه [Files] ایجاد می‌کند. در داخل این پوشه، آرشیو پروژه [mv-jsf2-02-1.0-SNAPSHOT.war] را پیدا خواهید کرد. این آرشیو است که روی سرور مستقر می‌شود،
  • در [WEB-INF / classes] و [2]، کلاس‌های کامپایل‌شده از پوشه [Source Packages] پروژه و همچنین فایل‌هایی را که در شاخه [Other Sources] بوده‌اند، خواهید یافت – در این مورد، فایل‌های پیام،
  • در [WEB-INF / lib] و [3]، کتابخانه‌های پروژه را خواهید یافت،
  • در ریشهٔ [WEB-INF] و [4]، فایل‌های پیکربندی پروژه را خواهید یافت،
  • در ریشهٔ آرشیو [5]، صفحاتی را که در شاخهٔ [Web Pages] پروژه بوده‌اند، پیدا خواهید کرد؛
  • پس از ساخت پروژه، می‌توان آن را اجرا کرد ([6]). این پروژه بر اساس پیکربندی زمان اجرای خود ([7]) اجرا خواهد شد،
  • سرور Tomcat در صورتی که از قبل در حال اجرا نبود، راه‌اندازی خواهد شد ([8])،
  • آرشیو [mv-jsf2-02-1.0-SNAPSHOT.war] به سرور بارگذاری خواهد شد. این کار به عنوان استقرار پروژه روی سرور برنامه شناخته می‌شود،
  • در [9]، به سیستم دستور داده می‌شود که هنگام اجرا یک مرورگر را راه‌اندازی کند. مرورگر، زمینهٔ برنامه را در [10]، c.a.d درخواست خواهد کرد. URL و [http://localhost:8080/mv-jsf2-02]. طبق قوانین موجود در فایل [web.xml] (به صفحه 44 مراجعه کنید)، این فایل [faces/index.xhtml] است که به مرورگر مشتری ارائه می‌شود. از آنجا که URL از نوع [/faces/*] است، توسط کنترلر [Faces Servlet] پردازش خواهد شد (به [web.xml] در صفحهٔ 44 مراجعه کنید). این کنترل‌کننده صفحه را پردازش کرده و جریان زیر HTML را ارسال می‌کند:
 
  • کنترل‌کننده [Faces Servlet] سپس رویدادهای رخ‌داده در این صفحه را مدیریت خواهد کرد.

2.4.9. فایل پیکربندی [faces-config.xml]

ما از فایل زیر استفاده کردیم: [faces-config.xml]:


<?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>
  </application>
</faces-config>

این فایل پیکربندی حداقلی برای یک برنامه JSF 2 بین‌المللی است. در اینجا، ما از ویژگی‌های جدید JSF 2 در مقایسه با JSF 1 استفاده کرده‌ایم:

  • اعلام کردن بین‌ها و دامنهٔ آن‌ها با استفاده از anotationهای @ManagedBean، @RequestScoped، @SessionScoped، @ApplicationScoped،
  • برای پیمایش بین صفحات، از نام صفحات XHTML (بدون پسوند xhtml) به عنوان کلیدهای ناوبری استفاده کنید.

ممکن است بخواهید از این گزینه‌ها اجتناب کنید و این عناصر پروژه JSF را در [faces-config.xml] و همچنین در JSF 1 اعلام کنید. در این صورت، فایل [faces-config.xml] ممکن است به شکل زیر باشد:


<?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>
  </application>
  
  <!-- بیان‌های مدیریت‌شده -->
  <managed-bean>
    <managed-bean-name>changeLocale</managed-bean-name>
    <managed-bean-class>utils.ChangeLocale</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

   <!-- ناوبری -->
  <navigation-rule>
    <description/>
    <from-view-id>/index.xhtml</from-view-id>
    <navigation-case>
      <from-outcome>p1</from-outcome>
      <to-view-id>/page1.xhtml</to-view-id>
    </navigation-case>
  </navigation-rule>

  <navigation-rule>
    <description/>
    <from-view-id>/page1.xhtml</from-view-id>
    <navigation-case>
      <from-outcome>welcome</from-outcome>
      <to-view-id>/index.xhtml</to-view-id>
    </navigation-case>
  </navigation-rule>


</faces-config>
  • خطوط ۲۰–۲۴: اعلان bean changeLocale:
    • خط ۲۱: نام bean؛
    • خط ۲۲: نام کامل کلاس مرتبط با بین؛
    • خط ۲۳: دامنهٔ bean. مقادیر ممکن عبارتند از request, session, application,
  • خطوط ۲۷–۳۴: اعلان یک قاعده ناوبری:
    • خط ۲۸: قاعده می‌تواند توصیف شود. در اینجا این کار انجام نشده است؛
    • خط ۲۹: صفحه‌ای که ناوبری از آن انجام می‌شود (نقطه شروع)؛
    • خطوط ۳۰–۳۳: یک مورد ناوبری. ممکن است چندین مورد وجود داشته باشد؛
    • خط ۳۱: کلید ناوبری؛
    • خط ۳۲: صفحه‌ای که ناوبری به آن انجام می‌شود.

قوانین ناوبری را می‌توان به شکلی بصری‌تر نمایش داد. هنگامی که فایل [faces-config.xml] در حال ویرایش است، می‌توانید از برگه [PageFlow] استفاده کنید:

 

فرض کنیم از فایل قبلی [faces-config.xml] استفاده می‌کنیم. اپلیکیشن ما چگونه تغییر خواهد کرد؟

  • در کلاس [ChangeLocaleanotationهای @ManagedBean و @SessionScoped ناپدید می‌شوند، زیرا اکنون bean در [faces-config] تعریف شده است،
  • پیمایش از [index.xhtml] به [page1.xhtml] از طریق یک لینک به این صورت خواهد شد:

        <h:commandLink value="#{msg['welcome.page1']}" action="p1"/>

ویژگی 'action' به کلید ناوبری 'p1' که در [faces-config] تعریف شده است، اختصاص داده می‌شود،

  • ناوبری از [page1.xhtml] به [index.xhtml] از طریق یک لینک به صورت زیر درمی‌آید:

        <h:commandLink value="#{msg['page1.welcome']}" action="welcome"/>

ویژگی 'action' به کلید ناوبری 'welcome' که در [faces-config] تعریف شده است، اختصاص داده شده است؛

  • متدهای setFrenchLocale و setEnglishLocale، که باید یک کلید ناوبری بازگردانند، نیازی به اصلاح ندارند زیرا قبلاً `null` را برای نشان دادن اینکه کاربر در همان صفحه باقی مانده است، برمیگرداندند.

2.4.10. نتیجه‌گیری

بیایید به پروژه NetBeans که نوشته‌ایم بازگردیم:

این پروژه شامل معماری زیر است:

در هر پروژه JSF، ما عناصر زیر را پیدا خواهیم کرد:

  • صفحات JSF و [A] که توسط کنترل‌کننده‌های [Faces Servlet] و [3] به مرورگرهای کلاینت ارسال می‌شوند،
  • فایل‌های پیام [C]، که امکان تغییر زبان صفحات JSF را فراهم می‌کنند،
  • کلاس‌های جاوا [B] که رویدادهای رخ‌داده در مرورگر مشتری [2a, 2b] و/یا به‌عنوان قالب برای صفحات JSF و [3] عمل می‌کنند. در اکثر موارد، لایه‌های [métier] و [DAO] به‌طور جداگانه توسعه و آزمایش می‌شوند. لایه [web] سپس با یک لایه نمونه [métier] آزمایش می‌شود. اگر لایه‌های [métier] و [DAO] در دسترس باشند، کار معمولاً با استفاده از آرشیوهای .jar آن‌ها انجام می‌شود.
  • از فایل‌های پیکربندی [D] برای پیوند دادن این عناصر مختلف به یکدیگر استفاده می‌شود. فایل [web.xml] در صفحهٔ ۴۴ توضیح داده شده و به ندرت تغییر خواهد کرد. همین امر در مورد [faces-config] نیز صدق می‌کند، جایی که ما همیشه از نسخهٔ ساده‌شده استفاده خواهیم کرد.

2.5. مثال mv-jsf2-03: فرم ورود داده – کامپوننت‌های JSF

از این پس، دیگر نحوه ساخت پروژه را نشان نخواهیم داد. ما پروژه‌های آماده را ارائه کرده و نحوه کار آن‌ها را توضیح خواهیم داد. خوانندگان می‌توانند تمام مثال‌ها را از وب‌سایت این سند دانلود کنند (به بخش 1.2 مراجعه کنید).

2.5.1. برنامه

برنامه یک نما دارد:

این برنامه اجزای اصلی JSF را که می‌توان در یک فرم ورود داده استفاده کرد، نمایش می‌دهد:

  • ستون [1] نام تگ JSF / HTML استفاده‌شده را نشان می‌دهد،
  • ستون [2] نمونه‌ای از ورود داده برای هر یک از تگ‌های مواجه شده را ارائه می‌دهد،
  • ستون [3] مقادیر بیانی را که به‌عنوان الگو برای صفحه استفاده می‌شوند، نمایش می‌دهد،
  • ورودی‌های انجام‌شده در [2] توسط دکمه [4] اعتبارسنجی می‌شوند. این اعتبارسنجی صرفاً مدل بین (model bean) صفحه را به‌روزرسانی می‌کند. سپس همان صفحه بازگردانده می‌شود. در نتیجه، پس از اعتبارسنجی، ستون [3] مقادیر جدید مدل بین را نمایش می‌دهد و بدین ترتیب به کاربر امکان می‌دهد تأثیر ورودی‌های خود را بر مدل صفحه بررسی کند.

2.5.2. پروژه NetBeans

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

  • در [1]، فایل‌های پیکربندی پروژه در JSF،
  • در [2]، صفحهٔ واحد پروژه: index.xhtml،
  • به [3]، یک stylesheet [styles.css] برای پیکربندی ظاهر صفحه [index.xhtml]
  • به [4]، کلاس‌های جاوا پروژه،
  • به [5]، فایل پیام برنامه به دو زبان: فرانسوی و انگلیسی.

2.5.3. فایل [pom.xml]

ما فقط وابستگی‌ها را نشان می‌دهیم:


    <dependencies>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
</dependencies>

اینها وابستگی‌های مورد نیاز برای پروژه JSF هستند. در مثال‌های زیر، این فایل تنها زمانی نمایش داده می‌شود که تغییر کند.

2.5.4. فایل [web.xml]

فایل [web.xml] طوری پیکربندی شده است که صفحه [index.xhtml] صفحه اصلی پروژه باشد:


<?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.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>  
  <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>
</web-app>
  • خط ۳۰: صفحه [index.xhtml] صفحه اصلی است،
  • خطوط ۱۱–۱۴: تنظیمی برای سروِلِت [Faces Servlet]. این مشخص می‌کند که نظرات در یک فیسِلت مانند:

        <!-- زبان‌ها -->

نادیده گرفته می‌شوند. بدون این پارامتر، این نظرات مشکلاتی ایجاد می‌کنند که درک آن‌ها دشوار است،

  • خطوط ۳–۶: یک پارامتر برای servlet [Faces Servlet] که کمی بعد توضیح داده خواهد شد.

2.5.5. فایل [faces-config.xml]

فایل [faces-config.xml] برنامه به شرح زیر است:


<?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>
  </application>
</faces-config>
  • خطوط ۱۱–۱۶: پیکربندی فایل پیام برنامه.

2.5.6. فایل پیام [messages.properties]

فایل‌های پیام (به [5] در تصویر پروژه مراجعه کنید) به شرح زیر هستند:

[messages_fr.properties]


form.langue1=Fran\u00e7ais
form.langue2=Anglais
form.titre=Java Server Faces - les tags
form.headerCol1=Type
form.headerCol2=Champs de saisie
form.headerCol3=Valeurs du modèle de la page
form.loginPrompt=login : 
form.passwdPrompt=mot de passe : 
form.descPrompt=description : 
form.selectOneListBox1Prompt=choix unique : 
form.selectOneListBox2Prompt=choix unique : 
form.selectManyListBoxPrompt=choix multiple : 
form.selectOneMenuPrompt=choix unique : 
form.selectManyMenuPrompt=choix multiple : 
form.selectBooleanCheckboxPrompt=marié(e) : 
form.selectManyCheckboxPrompt=couleurs préférées : 
form.selectOneRadioPrompt=moyen de transport préféré : 
form.submitText=Valider
form.buttonRazText=Raz

این پیام‌ها در مکان‌های زیر در صفحه نمایش داده می‌شوند:

نسخهٔ انگلیسی پیام‌ها به شرح زیر است:

[messages_en.properties]


form.langue1=French
form.langue2=English
form.titre=Java Server Faces - the tags
form.headerCol1=Input Type
form.headerCol2=Input Fields
form.headerCol3=Page Model Values
form.loginPrompt=login : 
form.passwdPrompt=password : 
form.descPrompt=description : 
form.selectOneListBox1Prompt=unique choice : 
form.selectOneListBox2Prompt=unique choice : 
form.selectManyListBoxPrompt=multiple choice : 
form.selectOneMenuPrompt=unique choice : 
form.selectManyMenuPrompt=multiple choice : 
form.selectBooleanCheckboxPrompt=married : 
form.selectManyCheckboxPrompt=preferred colors : 
form.selectOneRadioPrompt=preferred transport means : 
form.submitText=Submit
form.buttonRazText=Reset

2.5.7. قالب [Form.java] در صفحه [index.xhtml]

در پروژهٔ بالا، کلاس [Form.java] به‌عنوان الگو یا backing bean برای صفحهٔ JSF [index.xhtml] عمل خواهد کرد. بیایید این مفهوم الگو را با مثالی از صفحهٔ [index.xhtml] نشان دهیم:


<!-- خط ۱ -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
          <h:outputText value="#{form.inputText}"/>

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

ردیف ۲ فیلد [1] را نمایش می‌دهد؛ ردیف‌های ۳ تا ۶ فیلد [2] را نمایش می‌دهند؛ و ردیف ۷ فیلد [3] را نمایش می‌دهد.

خطوط ۵ و ۷ از عبارت EL استفاده می‌کنند که از bean فرم تعریف‌شده در کلاس [Form.java] به شرح زیر بهره می‌برد:


package forms;

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


@ManagedBean
@RequestScoped
public class Form {
  • خط ۷ یک بین بدون نام تعریف می‌کند. بنابراین این نام کلاس خواهد بود که با یک حرف کوچک شروع می‌شود: form,
  • این بین دارای دامنه درخواست است. این بدان معناست که در طول یک چرخه درخواست/پاسخ کلاینت/سرور، هنگام نیاز درخواست ایجاد شده و پس از بازگشت پاسخ به کلاینت، از بین می‌رود.

در کد زیر از صفحه [index.xhtml]:


<!-- خط 1 -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
<h:outputText value="#{form.inputText}"/>

خطوط ۵ و ۷ از مقدار inputText در bean «form» استفاده می‌کنند. برای درک ارتباط بین یک صفحه P و مدل آن M، باید به چرخه درخواست/پاسخ سرور-کلاینت که یک برنامه وب را مشخص می‌کند، بازگردیم:

باید بین موردی که صفحه P در پاسخ به مرورگر ارسال می‌شود (مرحله ۴) – برای مثال، در طول درخواست اولیه برای صفحه – و موردی که کاربر یک رویداد را در صفحه P فعال می‌کند، که سپس توسط کنترلر [Faces Servlet] (مرحله ۱) پردازش می‌شود، تمایز قائل شد.

این دو حالت را می‌توان با در نظر گرفتن آن‌ها از دیدگاه مرورگر متمایز کرد:

  1. در طول درخواست اولیه برای صفحه، مرورگر عملی GET را روی URL صفحه انجام می‌دهد،
  2. وقتی مقادیر وارد شده در صفحه ارسال می‌شوند، مرورگر یک عملیات POST را روی URL صفحه انجام می‌دهد.

در هر دو مورد، یک URL یکسان درخواست می‌شود. بسته به ماهیت درخواست مرورگر (GET یا POST)، پردازش درخواست متفاوت خواهد بود.

[cas 1 – demande initiale de la page P]

مرورگر با استفاده از GET، URL را برای صفحه درخواست می‌کند. کنترل‌کننده [Faces Servlet] مستقیماً به مرحله [4] رندر کردن پاسخ می‌رود و صفحه [index.xhtml] برای کلاینت ارسال می‌شود. کنترل‌کننده JSF به هر تگ روی صفحه دستور نمایش را صادر می‌کند. برای مثال خط ۵ از کد [index.xhtml] را در نظر بگیرید:


            <h:inputText id="inputText" value="#{form.inputText}"/>

تگ JSF <h:inputText value="value"/> تگ HTML <input type="text" value="value"/> را تولید می‌کند. کلاسی که مسئول پردازش این تگ است با عبارت #{form.inputText} مواجه می‌شود که باید آن را ارزیابی کند:

  • اگر فرم‌بین هنوز وجود نداشته باشد، با نمونه‌سازی کلاس forms.Form ایجاد می‌شود،
  • بیان #{form.inputText} با فراخوانی متد form.getInputText() ارزیابی می‌شود،
  • متن <input id="form:inputText" type="text" name="form:inputText" value="text" /> در جریان HTML که به کلاینت ارسال خواهد شد، درج می‌شود، با این فرض که متد form.getInputText() رشته "text" را بازگردانده باشد. JSF همچنین به مؤلفه HTML که در جریان گنجانده شده است، یک نام اختصاص می‌دهد. این نام از شناسه‌های «id» کامپوننت تحلیل‌شده JSF و شناسه‌های کامپوننت‌های والد آن، در این مورد تگ <h:form id="formulaire"/>، ساخته می‌شود.

توجه داشته باشید که اگر در صفحه‌ای با نام P، عبارت #{M.champ} استفاده شود – که در آن M، مدل (bean) برای صفحه P است – این مدل باید یک متد عمومی getChamp() داشته باشد. نوع بازگشتی این متد باید قابل تبدیل به نوع String باشد. یک مدل M ممکن و رایج به شرح زیر است:

1
2
3
4
private T champ;
public T getChamp(){
    return champ;
} 

که در آن T نوعی است که می‌تواند به نوع String تبدیل شود، احتمالاً با استفاده از متد toString.

با این حال، در مورد نمایش صفحه P، پردازش خط:


<h:outputText value="#{form.inputText}"/>

شبیه خواهد بود، و استریم زیر HTML ایجاد خواهد شد:

texte

درون سرور، صفحه P به‌صورت درختی از کامپوننت‌ها نمایش داده می‌شود که بازتاب‌دهنده درخت تگ‌های صفحه‌ای است که به کلاینت ارسال شده است. ما به این درخت، نمای صفحه یا حالت صفحه می‌گوییم. این حالت ذخیره می‌شود. این حالت می‌تواند به دو روش ذخیره شود، بسته به پیکربندی‌ای که در فایل [web.xml] برنامه تنظیم شده است:


<web-app ...>
...
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</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>
...
</web-app>

خطوط ۷ تا ۱۱ کنترل‌کننده [Faces Servlet] را تعریف می‌کنند. این کنترل‌کننده را می‌توان با استفاده از تگ‌های مختلف <context-param>، از جمله تگ‌های موجود در خطوط ۳ تا ۶، پیکربندی کرد. این تگ‌ها مشخص می‌کنند که وضعیت یک صفحه باید در سمت کلاینت (مرورگر) ذخیره شود. مقدار دیگر ممکن، در خط ۵، 'server' است که نشان می‌دهد وضعیت باید روی سرور ذخیره شود. این مقدار پیش‌فرض است.

وقتی وضعیت یک صفحه روی کلاینت ذخیره می‌شود، کنترل‌کننده JSF یک فیلد مخفی به هر صفحه HTML که ارسال می‌کند اضافه می‌کند که مقدار آن وضعیت فعلی صفحه است. این فیلد مخفی فرمت زیر را دارد:

<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...Bnoz8dqAAA=" />

مقدار آن، وضعیت صفحه را به صورت رمزگذاری‌شده نشان می‌دهد. مهم است که درک کنیم این فیلد مخفی بخشی از فرم صفحه را تشکیل می‌دهد و بنابراین هنگام ارسال فرم توسط مرورگر، در میان مقادیر ارسال‌شده گنجانده خواهد شد. کنترل‌کننده JSF با استفاده از این فیلد مخفی، قادر است نما را همان‌طور که به کلاینت ارسال شده بود، بازیابی کند.

هنگامی که وضعیت یک صفحه روی سرور ذخیره می‌شود، وضعیت صفحه‌ای که به کلاینت ارسال شده است در جلسه (session) کلاینت ذخیره می‌گردد. هنگامی که مرورگر کلاینت مقادیر وارد شده در فرم را ارسال می‌کند، توکن جلسه خود را نیز ارسال خواهد کرد. با استفاده از این توکن، کنترل‌کننده JSF وضعیت صفحه‌ای را که به کلاینت ارسال شده است بازیابی کرده و آن را بازیابی می‌کند.

وضعیت یک صفحه JSF ممکن است برای رمزگذاری به چند صد بایت نیاز داشته باشد. از آنجایی که این وضعیت برای هر کاربر برنامه حفظ می‌شود، در صورت وجود تعداد زیاد کاربر، ممکن است مشکلات حافظه پیش آید. به همین دلیل، ما در اینجا تصمیم گرفته‌ایم وضعیت صفحه را در سمت کلاینت ذخیره کنیم (به [web.xml]، بند 2.5.4، صفحه 66 مراجعه کنید).

[cas 2 – traitement de la page P]

ما در مرحله [1] بالا هستیم، جایی که کنترل‌کننده [Faces Servlet] درخواستی POST را از مرورگر کلاینت دریافت خواهد کرد که پیش از این صفحه [index.xhtml] را برای آن ارسال کرده بود. این شامل پردازش یک رویداد صفحه می‌شود. چندین مرحله قبل از اینکه رویداد حتی بتواند در [2a] پردازش شود، انجام خواهد شد. چرخه پردازش برای یک درخواست POST توسط کنترل‌کننده JSF به شرح زیر است:

FEDCBA

Image

  • در [A]، به لطف فیلد مخفی javax.faces.ViewState، نمایی که در ابتدا به مرورگر کلاینت ارسال شده بود، بازسازی می‌شود. در اینجا، اجزای صفحه به مقادیری که در صفحه‌ی ارسال‌شده داشتند، بازگردانده می‌شوند. کامپوننت ما inputText به مقدار «text» خود بازمی‌گردد،
  • در [B]، مقادیری که توسط مرورگر کلاینت ارسال شده‌اند برای به‌روزرسانی اجزای نما استفاده می‌شوند. بنابراین، اگر کاربر در فیلد ورودی HTML با نام inputText، کلمه «jean» را تایپ کرده باشد، مقدار «jean» جایگزین مقدار «text» می‌شود. اکنون نما، صفحه را همان‌طور که توسط کاربر اصلاح شده است نشان می‌دهد، نه همان‌طور که به مرورگر ارسال شده بود،
  • در [C]، مقادیر ارسال‌شده بررسی می‌شوند. فرض کنید که مؤلفه قبلی، inputText، یک فیلد ورودی سن است. مقداری که وارد می‌شود باید یک عدد صحیح باشد. مقادیر ارسال‌شده توسط مرورگر همیشه از نوع String هستند. نوع نهایی آن‌ها در مدل M مرتبط با صفحه P ممکن است کاملاً متفاوت باشد. سپس تبدیل از نوع String به نوع دیگر، T، انجام می‌شود. این تبدیل ممکن است ناموفق باشد. در این حالت، چرخه درخواست/پاسخ خاتمه می‌یابد و صفحه P، که در [B] ساخته شده است، به همراه پیام‌های خطا (در صورتی که نویسنده صفحه P آن‌ها را فراهم کرده باشد) به مرورگر مشتری بازگردانده می‌شود. شایان ذکر است که کاربر صفحه را دقیقاً همان‌طور که وارد کرده است می‌بیند، بدون هیچ تلاشی از سوی توسعه‌دهنده. در فناوری دیگری، مانند JSP، توسعه‌دهنده باید صفحه P را خود با استفاده از مقادیری که کاربر وارد کرده است، بازسازی کند. مقدار یک مؤلفه ممکن است تحت فرآیند اعتبارسنجی نیز قرار گیرد. هنوز با استفاده از مثال کامپوننت inputText که یک فیلد ورودی سن است، مقداری که وارد می‌شود نه تنها باید یک عدد صحیح باشد، بلکه باید عددی صحیح در محدوده [1,N] باشد. اگر مقدار وارد شده مرحله تبدیل را با موفقیت پشت سر بگذارد، ممکن است مرحله اعتبارسنجی را پشت سر نگذارد. در این حالت نیز، چرخه درخواست/پاسخ کامل می‌شود و صفحه P، که در [B] تولید شده است، به مرورگر کلاینت بازگردانده می‌شود،
  • در [D]، اگر همهٔ اجزای صفحهٔ P مرحلهٔ تبدیل و اعتبارسنجی را با موفقیت پشت سر بگذارند، مقادیر آن‌ها به قالب M صفحهٔ P اختصاص داده خواهد شد. اگر مقدار فیلد ورودی تولیدشده از تگ زیر باشد:

        <h:inputText value="#{form.inputText}"/>

«jean» باشد، آنگاه این مقدار با اجرای کد form.setInputText("jean") به مدل فرم صفحه تخصیص داده می‌شود. شایان ذکر است که در مدل M صفحه P، فیلدهای خصوصی M که مقدار یک فیلد ورودی از P را ذخیره می‌کنند، باید دارای یک متد set باشند؛

  • پس از به‌روزرسانی مدل M صفحه P با مقادیر ارسال‌شده، رویدادی که POST را در صفحه P تحریک کرده است، قابل پردازش است. این مرحله [E] است. شایان ذکر است که اگر دست‌پرداز این رویداد متعلق به بین M باشد، به مقادیر فرم P که در فیلدهای همان بین ذخیره شده‌اند، دسترسی دارد.
  • مرحله [E] یک کلید ناوبری را به کنترل‌کننده JSF بازمی‌گرداند. در مثال‌های ما، این همیشه نام صفحه XHTML خواهد بود که باید نمایش داده شود، بدون پسوند .xhtml. این مرحله [F] است. راه دیگر برای انجام این کار، بازگرداندن یک کلید ناوبری است که در فایل [faces-config.xml] جستجو خواهد شد. ما این سناریو را شرح داده‌ایم.

از موارد فوق می‌توان نتیجه گرفت که:

  • صفحه P فیلدهای C قالب M خود را با استفاده از متدهای [M].getC() نمایش می‌دهد،
  • میدان‌های C از مدل M برای یک صفحه P با مقادیر وارد شده در صفحه P با استفاده از متدهای [M].setC(entry) مقداردهی اولیه می‌شوند. در این مرحله، فرآیندهای تبدیل و اعتبارسنجی ممکن است انجام شوند که ممکن است با شکست مواجه شوند. در این حالت، رویدادی که POST را برای صفحه P تحریک کرده است، پردازش نمی‌شود و صفحه دقیقاً همان‌طور که توسط کلاینت وارد شده است، به کلاینت بازگردانده می‌شود.

قالب [Form.java] برای صفحه [index.xhtml] به شرح زیر خواهد بود:


package forms;

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


@ManagedBean
@RequestScoped
public class Form {
  
  /** یک نمونه جدید از فرم ایجاد می‌کند */
  public Form() {
  }
  
  // fields of form
  private String inputText="texte";
  private String inputSecret="secret";
  private String inputTextArea="ligne1\nligne2\n";
  private String selectOneListBox1="2";
  private String selectOneListBox2="3";
  private String[] selectManyListBox=new String[]{"1","3"};
  private String selectOneMenu="1";
  private String[] selectManyMenu=new String[]{"1","2"};
  private String inputHidden="initial";
  private boolean selectBooleanCheckbox=true;
  private String[] selectManyCheckbox=new String[]{"1","3"};
  private String selectOneRadio="2";
  
  // رویدادها
  public String submit(){
    return null;
  }
  
  // گیرنده‌ها و تنظیم‌کننده‌ها
  ...
}

میدان‌های خطوط ۱۶–۲۷ در مکان‌های زیر در فرم استفاده می‌شوند:

2.5.8. صفحه [index.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:f="http://java.sun.com/jsf/core">

  <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">
        <!-- زبان‌ها -->
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['form.titre']}"/></h1>
        <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
          <!-- سربرگ‌ها -->
          <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
          <!-- خط ۱ -->
          ...
          <!-- خط ۲ -->
          ...
          <!-- خط ۳ -->
          ...
          <!-- خط ۴ -->
          ...
          <!-- خط ۵ -->
          ...
          <!-- خط ۶ -->
          ...
          <!-- خط ۷ -->
          ...
          <!-- خط ۸ -->
          ...
          <!-- خط ۹ -->
          ...
          <!-- خط ۱۰ -->
          ...
          <!-- خط ۱۱ -->
          ...
          <!-- خط ۱۲ -->
          ...
        </h:panelGrid>
        <p>
          <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
        </p>
      </h:form>
    </h:body>
  </f:view>
</html>

اکنون اجزای اصلی این صفحه را به ترتیب بررسی می‌کنیم. ساختار کلی یک فرم JSF را توجه کنید:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

  <f:view ...>
    <h:head>
      ...
    </h:head>
    <h:body ...>
      <h:form id="formulaire">
        ...
        <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
        ...
      </h:form>
    </h:body>
  </f:view>
</html>

اجزای یک فرم باید در داخل تگ <h:form> (خطوط ۱۲–۱۶) قرار گیرند. تگ <f:view> (خطوط ۷–۱۸) در صورتی که برنامه قرار است بومی‌سازی شود، الزامی است. علاوه بر این، یک فرم باید راهی برای ارسال داشته باشد (POST)، که اغلب یک لینک یا دکمه است، همانطور که در خط 14 نشان داده شده است. همچنین می‌توان آن را از طریق رویدادهای مختلف ارسال کرد (تغییر در یک انتخاب در لیست، تغییر در فیلد فعال، تایپ یک کاراکتر در یک فیلد ورودی و غیره).

2.5.9. سبک فرم

برای خواناتر کردن ستون‌های جدول فرم، یک فایل شیوه‌نامه همراه آن ارائه می‌شود:


  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
</h:head>
  • خط ۴: فایل سبک صفحه در تگ <head> HTML با استفاده از تگ زیر تعریف می‌شود:

<h:outputStylesheet library="css" name="styles.css"/>

فایل سبک در پوشه [resources] جستجو خواهد شد:

در تگ:


<h:outputStylesheet library="css" name="styles.css"/>
  • «library» نام پوشه‌ای است که شامل فایل سبک می‌باشد،
  • 'name' نام صفحه‌ی سبک است.

بیایید نگاهی به یک مثال از نحوهٔ استفاده از این شیوه‌نامه بیندازیم:


        <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">

تگ <h:panelGrid columns="3"/> یک جدول با سه ستون را تعریف می‌کند. ویژگی columnClasses برای استایل‌دهی این ستون‌ها استفاده می‌شود. مقادیر col1، col2 و col3 از ویژگی columnClasses به سبک‌های مربوط به ستون‌های 1، 2 و 3 جدول اشاره دارند. این سبک‌ها در صفحه‌شیوهٔ صفحه جستجو می‌شوند:


.info{
   font-family: Arial,Helvetica,sans-serif;
   font-size: 14px;
   font-weight: bold
}

.col1{
   background-color: #ccccff
}

.col2{
   background-color: #ffcccc
}

.col3{
   background-color: #ffcc66
}

.entete{
   font-family: 'Times New Roman',Times,serif;
   font-size: 14px;
   font-weight: bold
}
  • خطوط ۷–۹: استایلی به نام col1،
  • خطوط ۱۱–۱۳: استایلی به نام col2،
  • خطوط ۱۵–۱۷: استایلی به نام col3،

این سه استایل رنگ پس‌زمینه هر ستون را تعریف می‌کنند.

  • خطوط ۱۹–۲۳: سبک «entete» برای تعریف سبک متن در ردیف اول جدول استفاده می‌شود:

          <!-- entêtes -->
          <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
  • خطوط ۱–۵: سبک 'info' برای تعریف سبک متن در ستون اول جدول استفاده می‌شود:

          <!-- خط 1 -->
          <h:outputText value="inputText"  styleClass="info"/>

ما در مورد استفاده از صفحات سبک (style sheets) زیاد وارد جزئیات نمی‌شویم، زیرا خودِ آن‌ها شایستهٔ یک کتاب مستقل هستند و علاوه بر این، ایجادشان اغلب به متخصصان سپرده می‌شود. با این حال، ما استفاده از یک سبک مینیمالیستی را برای تأکید بر این نکته که استفاده از آن‌ها ضروری است، انتخاب کرده‌ایم.

اکنون بیایید ببینیم تصویر پس‌زمینه صفحه چگونه تعریف شده است:


<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">

تصویر پس‌زمینه با استفاده از ویژگی style تگ <h:body> تنظیم می‌شود. این ویژگی به شما امکان می‌دهد عناصر استایل را مشخص کنید. تصویر پس‌زمینه در پوشه [resources/images/standard.jpg] قرار دارد:

این تصویر از طریق URL و [/mv-jsf2-03/resources/images/standard.jpg] به دست می‌آید. بنابراین می‌توانیم بنویسیم:


<h:body style="background-image: url('mv-jsf2-03/resources/images/standard.jpg');">

/mv-jsf2-03 زمینهٔ برنامه است. این زمینه توسط مدیر وب‌سرور تنظیم می‌شود و بنابراین ممکن است تغییر کند. می‌توان آن را با استفاده از عبارت EL ${request.contextPath} به دست آورد. بنابراین ما ویژگی style زیر را ترجیح می‌دهیم:


style="background-image: url('${request.contextPath}/resources/images/standard.jpg');"

که صرف‌نظر از زمینه معتبر خواهد بود.

2.5.10. دو چرخه درخواست-کلاینت/پاسخ-سرور برای یک فرم

بیایید به آنچه در پاراگراف 2.5.7 در یک زمینه کلی توضیح داده شده است بازگردیم و آن را بر روی فرم مورد نظر اعمال کنیم. این فرم در محیط استاندارد JSF آزمایش خواهد شد:

در اینجا هیچ رویدادپرداز یا لایه [métier] وجود نخواهد داشت. بنابراین مراحل [2x] وجود نخواهند داشت. بین موردی که فرم F در ابتدا توسط مرورگر درخواست می‌شود و موردی که کاربر رویدادی را در فرم F فعال کرده است و سپس توسط کنترل‌کننده [Faces Servlet] پردازش می‌شود، تمایز قائل خواهیم شد. دو چرخه درخواست مشتری/پاسخ سرور متمایز وجود دارد.

  • اولین چرخه که مربوط به درخواست اولیه برای صفحه است، توسط یک عملیات GET از مرورگر بر روی URL فرم، آغاز می‌شود،
  • دومین چرخه، که مربوط به ارسال مقادیر وارد شده در صفحه است، توسط یک عملیات POST روی همین URL آغاز می‌شود.

بسته به ماهیت درخواست مرورگر (GET یا POST)، نحوه پردازش درخواست توسط کنترلر [Faces Servlet] متفاوت است.

[cas 1 – demande initiale du formulaire F]

مرورگر با یک GET، URL صفحه را درخواست می‌کند. کنترل‌کننده [Faces Servlet] مستقیماً به مرحله رندر پاسخ [4] می‌رود. فرم [index.xhtml] توسط قالب [Form.java] خود اولیه شده و به کلاینت ارسال می‌شود که نمای زیر را دریافت می‌کند:

Image

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

درخواست کلاینت HTTP:

1
2
3
4
5
6
7
8
GET /mv-jsf2-03/ HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en;q=0.6,en-us;q=0.4,es;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive

خط ۱ نشان‌دهنده GET مرورگر است.

پاسخ سرور HTTP:

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Powered-By: JSF/2.0
Set-Cookie: JSESSIONID=F6E66136BF00EEE026ADAB1BBEBFD587; Path=/mv-jsf2-03/; HTTPOnly
Content-Type: text/html;charset=UTF-8
Content-Length: 7371
Date: Tue, 15 May 2012 09:04:57 GMT

در اینجا نشان داده نشده است، پس از خط ۷ یک خط خالی آمده و کد فرم HTML قرار دارد. این کد است که مرورگر آن را تفسیر و نمایش می‌دهد.

[cas 2 – traitement des valeurs saisies dans le formulaire F]

کاربر فرم را تکمیل کرده و با استفاده از دکمه [Valider] آن را ارسال می‌کند. سپس مرورگر با ارسال یک POST، URL فرم را درخواست می‌کند. کنترل‌کننده [Faces Servlet] این درخواست را پردازش می‌کند، قالب [Form.java] را برای فرم [index.xhtml] به‌روزرسانی می‌کند و فرم [index.xhtml] را با این قالب جدید بازمی‌گرداند. بیایید این چرخه را با یک مثال بررسی کنیم:

Image

در مثال بالا، کاربر داده‌های خود را وارد و اعتبارسنجی کرده است. در پاسخ، نمای زیر را دریافت می‌کند:

Image

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

درخواست کلاینت HTTP:

POST /mv-jsf2-03/faces/index.xhtml HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en;q=0.6,en-us;q=0.4,es;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Referer: http://localhost:8080/mv-jsf2-03/faces/index.xhtml
Cookie: JSESSIONID=374CC5F1D2ACAC182A5747A443651E36
Content-Type: application/x-www-form-URLencoded
Content-Length: 1543

formulaire=formulaire&formulaire%3AinputText=nouveau+texte&formulaire%3AinputSecret=mdp&formulaire%3AinputTextArea=Tutoriel+JSF%0D%0A&formulaire%3AselectOneListBox1=3&formulaire%3AselectOneListBox2=5&formulaire%3AselectManyListBox=3&formulaire%3AselectManyListBox=4&formulaire%3AselectManyListBox=5&formulaire%3AselectOneMenu=4&formulaire%3AselectManyMenu=5&formulaire%3AinputHidden=initial&formulaire%3AselectManyCheckbox=2&formulaire%3AselectManyCheckbox=3&formulaire%3AselectManyCheckbox=4&formulaire%3AselectOneRadio=4&formulaire%3Asubmit=Valider&javax.faces.ViewState=H4sIAAAAAAAAAJVUT0g...P4BKm1E4F0FAAA  

در خط ۱، POST توسط مرورگر تولید می‌شود. در خط ۱۴، مقادیر وارد شده توسط کاربر هستند. برای مثال، متنی که در فیلد ورودی وارد شده است را می‌توان در اینجا مشاهده کرد:

formulaire%3AinputText=nouveau+texte

در خط ۱۴، فیلد مخفی javax.faces.ViewState ارسال شده است. این فیلد، وضعیت فرم را به شکل رمزگذاری‌شده، همان‌طور که در درخواست اولیه GET به مرورگر ارسال شده بود، نشان می‌دهد.

پاسخ سرور HTTP:

1
2
3
4
5
6
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Powered-By: JSF/2.0
Content-Type: text/html;charset=UTF-8
Content-Length: 7299
Date: Tue, 15 May 2012 09:37:17 GMT

در اینجا نشان داده نشده است، اما پس از خط ۶ یک خط خالی و کد HTML برای فرم به‌روزرسانی‌شده با قالب جدیدش که از POST مشتق شده است، قرار دارد.

اکنون اجزای مختلف این فرم را بررسی خواهیم کرد.

2.5.11. برچسب <h:inputText>

برچسب <h:inputText> یک برچسب <input type="text" ...> تولید می‌کند.

کد زیر را در نظر بگیرید:


          <!-- خط ۱ -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
<h:outputText value="#{form.inputText}"/>

و قالب آن [Form.java]:


  private String inputText="texte";

  public String getInputText() {
    return inputText;
  }
  
  public void setInputText(String inputText) {
    this.inputText = inputText;
}

وقتی صفحه [index.html] برای اولین بار درخواست می‌شود، صفحه حاصل به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند،
  • تگ <h:panelGroup> (خطوط ۳–۶) امکان گروه‌بندی چندین عنصر را در یک سلول از جدولی که توسط تگ <h:panelGrid> در خط ۲۰ کد کامل صفحه ایجاد شده است، فراهم می‌کند (به بخش ۲.۵.۸ مراجعه کنید). متن [2] توسط خط ۴ تولید می‌شود. فیلد ورودی [3] توسط خط [5] تولید می‌شود. در اینجا، متد getInputText از [Form.java] (خطوط ۳–۵ از کد جاوا) برای تولید متن فیلد ورودی استفاده شده است،
  • خط ۷ از کد XHTML، [4] را تولید می‌کند. بار دیگر، متد getInputText از [Form.java] برای تولید متن [4] استفاده می‌شود.

جریان HTML تولیدشده توسط صفحه XHTML به شرح زیر است:


<tr>
<td class="col1"><span class="info">inputText</span></td>
<td class="col2">login : <input id="formulaire:inputText" type="text" name="formulaire:inputText" value="texte" /></td>
<td class="col3">texte</td>
</tr>

تگ‌های و در HTML توسط تگ <h:panelGrid> که برای تولید جدول فرم استفاده می‌شود، تولید می‌شوند.

اکنون، در زیر، بیایید مقداری را در فیلد ورودی [1] وارد کرده و فرم را با استفاده از دکمه [Valider] [2] ارسال کنیم. در پاسخ، صفحه [3, 4] را دریافت می‌کنیم:

مقدار فیلد [1] به شرح زیر ارسال می‌شود:

formulaire%3AinputText=nouveau+texte

در [2]، فرم با استفاده از دکمه زیر ارسال می‌شود:


          <h:commandButton id="submit" type="submit" value="#{msg['form.submitText']}"/>

تگ <h:commandButton> فاقد ویژگی action است. در این حالت، هیچ رویدادپردازشی فراخوانی نمی‌شود و هیچ قاعده ناوبری اعمال نمی‌گردد. پس از پردازش، همان صفحه بازگردانده می‌شود. بیایید چرخه پردازش آن را بررسی کنیم:

ABCDEF

Image

  • در [A]، صفحه P دقیقاً همان‌طور که ارسال شده بود، بازیابی می‌شود. این بدان معناست که کامپوننتی با شناسه inputText با مقدار اولیه «text» بازیابی می‌شود،
  • در [B]، مقادیری که توسط مرورگر ارسال شده‌اند (وارد شده توسط کاربر) به کامپوننت‌های صفحه P اختصاص داده می‌شوند. در اینجا، کامپوننت با شناسه inputText مقدار «text جدید» را دریافت می‌کند،
  • در [C]، تبدیل‌ها و اعتبارسنجی‌ها انجام می‌شود. در اینجا، هیچ‌کدام وجود ندارد. در مدل M، فیلد مرتبط با کامپوننت با شناسه inputText به شرح زیر است:

private String inputText="texte";

از آنجایی که مقادیر وارد شده از نوع String هستند، هیچ تبدیلی لازم نیست. علاوه بر این، هیچ قاعده اعتبارسنجی ایجاد نشده است. ما این موارد را در مرحله بعد ایجاد خواهیم کرد.

  • در [D]، مقادیر وارد شده به قالب اختصاص داده می‌شوند. فیلد inputText در [Form.java] روی «متن جدید» تنظیم شده است،
  • در [E]، هیچ اتفاقی نمی‌افتد زیرا هیچ رویدادپرداز (event handler) به دکمه [Valider] مرتبط نشده است.
  • در [F]، صفحه P دوباره به کلاینت ارسال می‌شود زیرا دکمه [Valider] فاقد ویژگی action است. سپس خطوط زیر از [index.xhtml] اجرا می‌شوند:

          <!-- خط ۱ -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
<h:outputText value="#{form.inputText}"/>

خطوط ۵ و ۷ از مقدار فیلد inputText در قالب استفاده می‌کنند که اکنون «متن جدید» است. بنابراین نمایش حاصل:

Image

2.5.12. برچسب <h:inputSecret>

تگ <h:inputSecret> یک HTML <input type="password" ...>. این یک فیلد ورودی مشابه تگ <h:JSF> است، با این تفاوت که هر کاراکتری که کاربر تایپ می‌کند به‌صورت بصری با ستاره (*) جایگزین می‌شود.

کد زیر را در نظر بگیرید:


          <!-- خط ۲ -->
          <h:outputText value="inputSecret"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.passwdPrompt']}"/>
            <h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
          </h:panelGroup>
<h:outputText value="#{form.inputSecret}"/>

و قالب آن در [Form.java]:


private String inputSecret="secret";

وقتی صفحه [index.xhtml] برای اولین بار درخواست می‌شود، صفحه حاصل به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند
  • متن [2] توسط خط ۴ تولید می‌شود. فیلد ورودی [3] توسط خط [5] تولید می‌شود. به طور معمول، باید از متد getInputSecret از کلاس [Form.java] برای تولید متن فیلد ورودی استفاده می‌شد. وقتی فیلد از نوع «password» باشد، استثنا وجود دارد. تگ <h:inputSecret> فقط برای خواندن ورودی استفاده می‌شود، نه برای نمایش آن.
  • خط ۷ کد XHTML، [4] را تولید می‌کند. در اینجا، متد getInputSecret از [Form.java] برای تولید متن [4] استفاده شده است (به خط ۱ کد جاوا مراجعه کنید).

جریان HTML تولیدشده توسط صفحه XHTML به شرح زیر است:


<tr>
<td class="col1"><span class="info">inputSecret</span></td>
<td class="col2">mot de passe : <input id="formulaire:inputSecret" type="password" name="formulaire:inputSecret" value="" /></td>
<td class="col3">secret</td>
</tr>
  • خط ۳: تگ HTML <input type="password" .../> که توسط تگ JSF <h:inputSecret> تولید شده است

حالا در زیر، بیایید مقداری را در فیلد ورودی [1] وارد کرده و فرم را با استفاده از دکمه [Valider] [2] ارسال کنیم. در پاسخ، صفحه [3] را دریافت می‌کنیم:

مقدار فیلد [1] به شرح زیر ارسال می‌شود:

formulaire%3AinputSecret=mdp

اعتبارسنجی فرم توسط [2] منجر به به‌روزرسانی قالب [Form.java] از طریق ورودی [1] شد. سپس مقدار 'mdp' به فیلد inputSecret در [Form.java] اختصاص داده شد. از آنجایی که فرم [index.xhtml] هیچ قاعده ناوبری یا رویدادپرداز تعریف نکرده است، پس از به‌روزرسانی قالب آن مجدداً نمایش داده می‌شود. سپس به نمایی که هنگام درخواست اولیه صفحه [index.xhtml] نمایش داده می‌شد بازمی‌گردیم، جایی که تنها مقدار فیلد inputSecret در قالب به [3] تغییر یافته است.

2.5.13. برچسب <h:inputTextArea>

برچسب <h:inputTextArea> یک برچسب HTML <textarea ...>text</textarea> تولید می‌کند. این یک فیلد ورودی مشابه تگ <h:JSF> است، با این تفاوت که در اینجا می‌توانید چندین خط متن تایپ کنید.

کد زیر را در نظر بگیرید:


          <!-- خط ۳ -->
          <h:outputText value="inputTextArea" styleClass="info"/>          
          <h:panelGroup>
            <h:outputText value="#{msg['form.descPrompt']}"/>
            <h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
          </h:panelGroup>         
<h:outputText value="#{form.inputTextArea}"/>

و قالب آن در [Form.java]:


private String inputTextArea="ligne1\nligne2\n";

وقتی صفحه [index.xhtml] برای اولین بار درخواست می‌شود، صفحه حاصل به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند،
  • متن [2] توسط خط ۴ تولید می‌شود. فیلد ورودی [3] توسط خط [5] تولید می‌شود. محتوای آن با فراخوانی متد getInputTextArea مدل تولید شده است، که مقداری را که در خط ۱ کد جاوا در بالا تعریف شده بود، بازگردانده است،
  • خط ۷ کد XHTML، [4] را تولید می‌کند. در اینجا، متد getInputTextArea از [Form.java] مجدداً استفاده شد. رشتهٔ «line1\nline2» شامل شکست‌های خط \n بود. این شکست‌های خط هنوز پابرجاست. با این حال، وقتی در جریان HTML قرار می‌گیرند، توسط مرورگرها به صورت فاصله‌های خالی نمایش داده می‌شوند. تگ <textarea> از HTML که [3] را نمایش می‌دهد، شکست‌های خط را به درستی تفسیر می‌کند.

فید HTML تولید شده توسط صفحه XHTML به شرح زیر است:


<tr>
<td class="col1"><span class="info">inputTextArea</span></td>
<td class="col2">description : <textarea id="formulaire:inputTextArea" name="formulaire:inputTextArea" rows="4">ligne1
ligne2
</textarea></td>
<td class="col3">ligne1
ligne2
</td>
</tr>
  • خطوط ۳–۵: تگ HTML <textarea>...</textarea> تولید شده توسط تگ JSF <h:inputTextArea>

حالا، در زیر، بیایید مقداری را در فیلد ورودی [1] وارد کرده و فرم را با استفاده از دکمه [Valider] [2] ارسال کنیم. در پاسخ، صفحه [3] را دریافت می‌کنیم:

مقدار فیلد ارسال‌شده [1] به شرح زیر است:

formulaire%3AinputTextArea=Tutoriel+JSF%0D%0Apartie+1%0D%0A

اعتبارسنجی فرم توسط [2] منجر به به‌روزرسانی قالب [Form.java] از طریق ورودی [1] شد. سپس فیلد textArea در [Form.java] به مقدار "Tutorial JSF\npart1" تنظیم شد. وقتی [index.xhtml] دوباره نمایش داده می‌شود، می‌توان دید که فیلد textArea در قالب واقعاً به [3] به‌روزرسانی شده است.

2.5.14. برچسب <h:selectOneListBox>

برچسب <h:selectOneListBox> یک برچسب HTML <select>...</select> تولید می‌کند. از نظر بصری، این یک فهرست کشویی یا فهرستی با نوار پیمایش ایجاد می‌کند.

کد زیر را در نظر بگیرید:


<!-- خط ۴ -->
          <h:outputText value="selectOneListBox (size=1)" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/>
            <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItem itemValue="1" itemLabel="un"/>
              <f:selectItem itemValue="2" itemLabel="deux"/>
              <f:selectItem itemValue="3" itemLabel="trois"/>
            </h:selectOneListbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneListBox1}"/>

و قالب آن در [Form.java]:


private String selectOneListBox1="2";

وقتی صفحه [index.xhtml] برای اولین بار درخواست می‌شود، صفحه حاصل به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند
  • متن [2] توسط خط ۴ تولید می‌شود. لیست کشویی [3] توسط خطوط [5-9] تولید می‌شود. این مقدارِ صفت size است که روی "1" تنظیم شده و باعث می‌شود لیست تنها یک آیتم را نمایش دهد. اگر این ویژگی وجود نداشته باشد، مقدار پیش‌فرض ویژگی size برابر با 1 است. آیتم‌های لیست توسط تگ‌های <f:selectItem> در خطوط 6–8 تولید شده‌اند. این تگ‌ها سینتکس زیر را دارند:

<f:selectItem itemValue="valeur" itemLabel="texte"/>

مقدار ویژگی itemLabel همان چیزی است که در لیست نمایش داده می‌شود. مقدار ویژگی itemValue، مقدار عنصر است. این مقدار است که در صورت انتخاب عنصر از لیست کشویی، به کنترلر [Faces Servlet] ارسال خواهد شد.

مورد نمایش‌داده‌شده به عنوان [3] با فراخوانی متد getSelectOneListBox1() (خط ۵) تعیین شد. نتیجه «2» به دست آمده (خط 1 کد جاوا) باعث شد که آیتم موجود در خط 7 لیست کشویی نمایش داده شود، زیرا ویژگی itemValue آن روی «2» تنظیم شده است،

  • خط ۱۱ کد XHTML، [4] را تولید می‌کند. در اینجا، متد getSelectOneListBox1 از [Form.java] مجدداً استفاده شد.

جریان HTML تولیدشده توسط صفحه XHTML به شرح زیر است:


<tr>
<td class="col1"><span class="info">selectOneListBox (size=1)</span></td>
<td class="col2">choix unique : <select id="formulaire:selectOneListBox1" name="formulaire:selectOneListBox1" size="1">
    <option value="1">un</option>
    <option value="2" selected="selected">deux</option>
    <option value="3">trois</option>
</select></td>
<td class="col3">2</td>
</tr>
  • خطوط ۳ و ۷: تگ HTML <select ...>...</select> تولید شده توسط تگ JSF <h:selectOneListBox>,
  • خطوط ۴–۶: تگ‌های HTML <option ...> ... </option> تولید شده توسط تگ‌های JSF <f:selectItem
  • خط ۵: انتخاب بودن عنصر با مقدار="2" در لیست با وجود ویژگی selected="selected" نشان داده می‌شود.

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

مقدار ارسال‌شده برای فیلد [1] به شرح زیر است:

formulaire%3AselectOneListBox1=3

اعتبارسنجی فرم توسط [2] باعث شد رکورد [Form.java] با ورودی [1] به‌روزرسانی شود. عنصر HTML


    <option value="3">trois</option>

انتخاب شد. مرورگر رشته «3» را به‌عنوان مقدار برای مؤلفه JSF که فهرست کشویی را تولید می‌کرد، ارسال کرد:


            <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">

کنترل‌کننده JSF از متد setSelectOneListBox1("3") برای به‌روزرسانی مدل لیست کشویی استفاده خواهد کرد. پس از این به‌روزرسانی، فیلد در مدل [Form.java]


        private String selectOneListBox1;

اکنون حاوی مقدار «3» است.

هنگامی که صفحه [index.xhtml] پس از پردازش مجدداً نمایش داده می‌شود، این مقدار باعث نمایشی می‌شود که در بالا نشان داده شده است ([3,4]):

  • این مقدار تعیین می‌کند که کدام مورد در لیست کشویی نمایش داده شود ([3])،
  • مقدار فیلد selectOneListBox1 به صورت [4] نمایش داده می‌شود.

بیایید یک واریانت از تگ <h:selectOneListBox> را در نظر بگیریم:


<!-- خط ۵ -->
          <h:outputText value="selectOneListBox (size=3)" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/>
            <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
              <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"/>
            </h:selectOneListbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneListBox2}"/>

قالب در [Form.java] برای تگ <h:selectOneListBox> در خط ۵ به شرح زیر است:


  private String selectOneListBox2="3";

وقتی صفحه [index.xhtml] برای اولین بار درخواست می‌شود، صفحه‌ای که بازگردانده می‌شود به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند،
  • متن [2] توسط خط ۴ تولید می‌شود. لیست دارای نوار اسکرول، [3]، توسط خطوط [5-11] تولید می‌شود. این مقدار ویژگی size="3" است که باعث ایجاد یک لیست با نوار پیمایش به جای لیست کشویی می‌شود. آیتم‌های لیست توسط تگ‌های <f:selectItem> در خطوط 6–8 تولید شده‌اند،

عنصر انتخاب‌شده در [3] با فراخوانی متد getSelectOneListBox2() (خط ۵) تعیین شد. نتیجهٔ «3» به‌دست‌آمده (خط 1 کد جاوا) باعث شد عنصر در خط 8 لیست نمایش داده شود، زیرا ویژگی itemValue آن روی «3» تنظیم شده است؛

  • خط ۱۳ کد XHTML، [4] را تولید می‌کند. در اینجا، متد getSelectOneListBox2 از [Form.java] مجدداً استفاده شده است.

جریان HTML تولیدشده توسط صفحه XHTML به شرح زیر است:


<tr>
<td class="col1"><span class="info">selectOneListBox (size=3)</span></td>
<td class="col2">choix unique : <select id="formulaire:selectOneListBox2" name="formulaire:selectOneListBox2" size="3">
    <option value="1">un</option>
    <option value="2">deux</option>
    <option value="3" selected="selected">trois</option>
    <option value="4">quatre</option>
    <option value="5">cinq</option>
</select></td>
<td class="col3">3</td>
</tr>
  • خط ۶: انتخاب عنصر با مقدار="3" در لیست با وجود ویژگی selected="selected" منعکس شده است.

حالا در زیر، بیایید یک مقدار جدید از لیست انتخاب کنیم و با استفاده از دکمه فرم را ارسال کنیم. در پاسخ صفحه [3] را دریافت می‌کنیم:

مقدار ارسال‌شده برای فیلد [1] به شرح زیر است:

formulaire%3AselectOneListBox2=5

اعتبارسنجی فرم توسط [2] باعث به‌روزرسانی قالب [Form.java] از طریق ورودی [1] شد. عنصر HTML


    <option value="5">cinq</option>

انتخاب شد. مرورگر رشته «5» را به‌عنوان مقدار برای مؤلفه JSF که فهرست کشویی را تولید می‌کرد، ارسال کرد:


            <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">

کنترل‌کننده JSF از متد setSelectOneListBox2("5") برای به‌روزرسانی مدل لیست استفاده خواهد کرد. بنابراین، پس از این به‌روزرسانی، فیلد


        private String selectOneListBox2;

اکنون حاوی مقدار «5» است.

وقتی صفحه [index.xhtml] پس از پردازش دوباره نمایش داده می‌شود، این مقدار باعث نمایشی می‌شود که در بالا نشان داده شده است ([3,4]):

  • این تعیین می‌کند که کدام مورد لیست باید انتخاب شود ([3])،
  • مقدار فیلد selectOneListBox2 به صورت [4] نمایش داده می‌شود.

2.5.15. تگ <h:selectManyListBox>

تگ <h:selectmanyListBox> یک تگ <select multiple="multiple">...</select> تولید می‌کند که به کاربر اجازه می‌دهد چندین مورد را از یک لیست انتخاب کند.

کد زیر را در نظر بگیرید:


<!-- خط ۶ -->
          <h:outputText value="selectManyListBox (size=3)"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/>
            <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
              <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"/>
            </h:selectManyListbox>
            <p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyListBoxValue}"/>

و قالب آن در [Form.java]:


private String[] selectManyListBox=new String[]{"1","3"};

وقتی صفحه [index.xhtml] برای اولین بار درخواست می‌شود، صفحه حاصل به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند
  • متن [2] توسط خط ۴ تولید می‌شود. لیست [3] توسط خطوط [5-11] تولید می‌شود. ویژگی size="3" تضمین می‌کند که لیست در هر زمان سه مورد از این عناصر را نمایش دهد. عناصر منتخب از لیست با فراخوانی متد getSelectManyListBox() (خط ۵) در مدل جاوا تعیین شدند. نتیجه به‌دست‌آمده {"1","3"} (خط ۱ کد جاوا) یک آرایه از عناصر از نوع String است. هر یک از این عناصر برای انتخاب یکی از موارد موجود در لیست استفاده می‌شود. در اینجا، مواردی که در خطوط 6 و 10 قرار دارند و ویژگی itemValue آنها در آرایه {"1","3"} است، انتخاب خواهند شد. این موضوع توسط [3] نشان داده شده است.
  • خط ۱۴ کد XHTML، [4] را تولید می‌کند. در اینجا، فراخوانی نه به متد getSelectManyListBox مدل جاوا لیست، بلکه به متد زیر getSelectManyListBoxValue انجام می‌شود:

private String[] selectManyListBox=new String[]{"1","3"};
  ...
  // گیرنده‌ها و تنظیم‌کننده‌ها
  
  public String getSelectManyListBoxValue(){
    return getValue(selectManyListBox);
  }
  
  private String getValue(String[] chaines){
    String value="[";
    for(String chaine : chaines){
      value+=" "+chaine;
    }
    return value+"]";
  }

اگر متد getSelectManyListBox فراخوانی شده بود، آرایه‌ای از String به دست می‌آمد. برای افزودن این عنصر به جریان HTML، کنترل‌کننده متد آن یعنی toString را فراخوانی می‌کرد. با این حال، هنگامی که این متد روی یک آرایه اعمال می‌شود، صرفاً «هش‌کد» آرایه را بازمی‌گرداند و نه لیست عناصر آن، که ما به آن نیاز داریم. بنابراین، ما از متد getSelectManyListBoxValue که در بالا ذکر شد، برای به دست آوردن یک رشته که نمایانگر محتویات آرایه است، استفاده می‌کنیم،

  • خط ۱۲ از کد XHTML دکمه [5] را تولید می‌کند. وقتی این دکمه کلیک می‌شود، کد جاوااسکریپت در ویژگی onclick اجرا می‌شود. این کد در صفحه HTML جاسازی خواهد شد که توسط کد JSF تولید می‌شود. برای درک این موضوع، باید ماهیت دقیق آن صفحه را بدانیم.

جریان HTML که توسط صفحه XHTML تولید می‌شود، به شرح زیر است:


<tr>
<td class="col1"><span class="info">selectManyListBox (size=3)</span></td>
<td class="col2">choix multiple : <select id="formulaire:selectManyListBox" name="formulaire:selectManyListBox" multiple="multiple" size="3">
    <option value="1" selected="selected">un</option>
    <option value="2">deux</option>
    <option value="3" selected="selected">trois</option>
    <option value="4">quatre</option>
    <option value="5">cinq</option>
</select>
            <p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
          </td>
<td class="col3">[ 1 3]</td>
</tr>
  • خطوط ۳ و ۹: تگ HTML <select multiple="multiple"...>...</select> تولیدشده توسط تگ JSF <h:selectManyListBox>. حضور ویژگی `multiple` است که نشان می‌دهد این یک لیست چندگزینه‌ای است؛
  • و این واقعیت که مدل لیست آرایه رشته‌ای {"1","3"} است، به این معنی است که موارد لیست در خطوط ۴ (value="1") و ۶ (value="3") دارای ویژگی selected="selected" هستند،
  • خط ۱۰: هنگامی که دکمه [Raz] کلیک می‌شود، کد جاوااسکریپت در ویژگی onclick اجرا می‌گردد. این صفحه در مرورگر توسط درختی از اشیاء نمایش داده می‌شود که اغلب به آن DOM (مدل شیء مستند) گفته می‌شود. هر شیء در این درخت از طریق ویژگی نام (name) خود برای کد جاوااسکریپت قابل دسترسی است. لیست در خط ۳ از کد HTML بالا با نام form:selectManyListBox نامگذاری شده است. خود فرم را می‌توان به روش‌های مختلفی ارجاع داد. در اینجا، با استفاده از نشانه this.form به آن ارجاع داده شده است، که در آن `this` به دکمه [Raz] اشاره دارد و this.form به فرمی اشاره می‌کند که این دکمه در آن قرار دارد. فرم لیست:selectManyListBox در همین فرم قرار دارد. بنابراین، نگاشت this.form['formulaire:selectManyListBox'] به مکان لیست در درخت مؤلفه‌های فرم اشاره می‌کند. شیء نمایانگر یک فهرست دارای صفت selectedIndex است که مقدار آن تعداد آیتم انتخاب‌شده در فهرست را نشان می‌دهد. این عدد از 0 شروع می‌شود تا اولین آیتم فهرست را نشان دهد. مقدار -1 نشان می‌دهد که هیچ موردی در لیست انتخاب نشده است. کد جاوااسکریپتی که مقدار ویژگی selectedIndex را روی -1 تنظیم می‌کند، در صورت وجود هر موردی در لیست، همه موارد را از حالت انتخاب خارج می‌کند.

اکنون، در زیر، بیایید مقادیر جدیدی را از لیست انتخاب کنیم (برای انتخاب چندین مورد در لیست، کلید Ctrl را هنگام کلیک کردن پایین نگه دارید) و فرم را با استفاده از دکمه [Valider] [2] ارسال کنیم. صفحهٔ حاصل به این صورت است:

مقدار فیلد ارسال‌شده [1] به شرح زیر است:

formulaire%3AselectManyListBox=3&formulaire%3AselectManyListBox=4&formulaire%3AselectManyListBox=5

اعتبارسنجی فرم توسط [2] باعث به‌روزرسانی قالب [Form.java] از طریق ورودی [1] شد. عناصر HTML


    <option value="3">trois</option>
    <option value="4">quatre</option>
    <option value="5">cinq</option>

انتخاب شده‌اند. مرورگر سه رشته «3»، «4» و «5» را به‌عنوان مقادیر برای مؤلفه JSF ارسال کرد که فهرست کشویی را تولید کرد:


            <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">

از متد setSelectManyListBox مدل برای به‌روزرسانی این مدل با مقادیر ارسال‌شده توسط مرورگر استفاده خواهد شد:


  private String[] selectManyListBox;
....
  public void setSelectManyListBox(String[] selectManyListBox) {
    this.selectManyListBox = selectManyListBox;
}

در خط ۳، می‌بینیم که پارامتر این متد یک آرایه از String است. در این مورد، این آرایه {"3","4","5"} خواهد بود. پس از این به‌روزرسانی، فیلد


        private String[] selectManyListBox;

اکنون حاوی آرایه {"3","4","5"} است.

وقتی صفحه [index.xhtml] پس از پردازش دوباره نمایش داده می‌شود، این مقدار باعث نمایش نشان داده شده در بالا ([3,4]) می‌شود:

  • این تعیین می‌کند که کدام موارد در لیست باید انتخاب شوند ([3])،
  • مقدار فیلد selectManyListBox به صورت [4] نمایش داده می‌شود.

2.5.16. برچسب <h:selectOneMenu>

برچسب <h:selectOneMenu> با برچسب <h:selectOneListBox size="1"> یکسان است. در مثال، کد JSF اجرا شده به شرح زیر است:


<!-- خط ۷ -->
          <h:outputText value="selectOneMenu" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneMenuPrompt']}"/>
            <h: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"/>
            </h:selectOneMenu>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneMenu}"/>

قالب تگ <h:selectOneMenu> در [Form.java] به شرح زیر است:


  private String selectOneMenu="1";

درخواست اولیه برای صفحه [index.xhtml]، کد فوق نما را تولید می‌کند:

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

مقدار ارسال‌شده برای فیلد [1] به شرح زیر است:

formulaire%3AselectOneMenu=4

2.5.17. برچسب <h:selectManyMenu>

تگ <h:selectManyMenu> با تگ <h:selectManyListBox size="1"> یکسان است. کد JSF که در مثال اجرا می‌شود به شرح زیر است:


<!-- خط ۸ -->
          <h:outputText value="selectManyMenu" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" />
            <h: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"/>
            </h:selectManyMenu>
            <p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>

قالب تگ <h:selectManyMenu> در [Form.java] به شرح زیر است:


    private String[] selectManyMenu=new String[]{"1","2"};

در پاسخ به درخواست اولیه برای صفحه [index.xhtml]، کد بالا صفحه را تولید می‌کند:

فهرست [1] شامل متون «one»، …، «five» است که موارد «one» و «two» انتخاب شده‌اند. کد تولیدشده HTML به شرح زیر است:


<tr>
<td class="col1"><span class="info">selectManyMenu</span></td>
<td class="col2"><span class="prompt">choix multiple : </span><select id="formulaire:selectManyMenu" name="formulaire:selectManyMenu" multiple="multiple" size="1">
    <option value="1" selected="selected">un</option>
    <option value="2" selected="selected">deux</option>
    <option value="3">trois</option>
    <option value="4">quatre</option>
    <option value="5">cinq</option>
</select>
            
            
            <p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
          </td>
<td class="col3"><span class="prompt">[ 1 2]</span></td>
</tr>

همانطور که در بالا مشاهده می‌شود، در خطوط ۴ و ۵، موارد «one» و «two» انتخاب شده‌اند (که با وجود ویژگی selected مشخص شده است).

ارائه اسکرین‌شات از یک اجرای نمونه دشوار است، زیرا موارد انتخاب‌شده را نمی‌توان در منو نمایش داد. از خوانندگان دعوت می‌شود تا خودشان این کار را امتحان کنند (برای انتخاب چندین مورد از لیست، کلید Ctrl را نگه داشته و کلیک کنید).

2.5.18. تگ <h:inputHidden>

تگ <h:inputHidden> هیچ نمایش بصری ندارد. هدف اصلی آن وارد کردن یک تگ <input type="hidden" value="..."/> در جریان HTML صفحه است. وقتی در داخل تگ <h:form> قرار می‌گیرند، مقادیرشان در میان اطلاعاتی که هنگام ارسال فرم به سرور فرستاده می‌شوند، قرار دارد. از آنجایی که این‌ها فیلدهای فرم هستند و کاربر نمی‌تواند آن‌ها را ببیند، به آن‌ها فیلدهای مخفی گفته می‌شود. هدف این فیلدها حفظ داده‌ها بین چرخه‌های مختلف درخواست/پاسخ همان کلاینت است:

  • کلاینت فرم F را درخواست می‌کند. سرور آن را برای کلاینت ارسال می‌کند و یک قطعه اطلاعات I را در یک فیلد پنهان C، در قالب <h:inputHidden id="C" value="I"/>، قرار می‌دهد،
  • هنگامی که کلاینت فرم F را تکمیل کرده و آن را برای سرور ارسال می‌کند، مقدار I فیلد C به سرور بازگردانده می‌شود. سپس سرور می‌تواند اطلاعات I را که در صفحه ذخیره کرده بود، بازیابی کند. این امر یک حافظه بین دو چرخه درخواست/پاسخ ایجاد می‌کند،
  • خود JSF از این تکنیک استفاده می‌کند. اطلاعات «I» که در فرم F ذخیره می‌کند، مقدار تمام اجزای آن است. برای این منظور از فیلد مخفی زیر استفاده می‌کند:

<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...8PswawAA" />

این فیلد مخفی javax.faces.ViewState نام دارد و مقدار آن یک رشته است که مقادیر تمام اجزای صفحه‌ای را که به کلاینت ارسال شده، به صورت رمزگذاری‌شده نشان می‌دهد. هنگامی که کلاینت صفحه را پس از وارد کردن داده‌ها در فرم بازمی‌گرداند، فیلد مخفی javax.faces.ViewState به همراه مقادیر وارد شده بازگردانده می‌شود. این امر به کنترل‌کننده JSF امکان می‌دهد تا صفحه را همان‌طور که در ابتدا ارسال شده بود، بازسازی کند. این مکانیزم در صفحه ۷۲ توضیح داده شده است.

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


<!-- خط ۹ -->
          <h:outputText value="inputHidden"  styleClass="info"/>
          <h:inputHidden id="inputHidden" value="#{form.inputHidden}"/>
          <h:outputText value="#{form.inputHidden}"/>

قالب تگ <h:inputHidden> در [Form.java] به شرح زیر است:


  private String inputHidden="initial";

این امر منجر به نمایش زیر می‌شود هنگامی که صفحه [index.xhtml] برای اولین بار درخواست می‌شود:

  • خط ۲، [1] را تولید می‌کند، خط ۴، [2] را تولید می‌کند. خط ۳ هیچ عنصر بصری تولید نمی‌کند.

کد تولیدشده HTML به شرح زیر است:


<tr>
<td class="col1"><span class="info">inputHidden</span></td>
<td class="col2"><input id="formulaire:inputHidden" type="hidden" name="formulaire:inputHidden" value="initial" /></td>
<td class="col3">initial</td>
</tr>

هنگامی که فرم ارسال می‌شود (POST)، مقدار «ابتدایی» فیلدی به نام form:inputHidden در خط ۳ به همراه سایر مقادیر فرم ارسال خواهد شد. فیلد


  private String inputHidden;

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

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

formulaire%3AinputHidden=initial

2.5.19. تگ <h:selectBooleanCheckBox>

تگ <h:selectBooleanCheckBox> یک تگ <input type="checkbox" ...> با مقدار HTML تولید می‌کند.

کد زیر JSF را در نظر بگیرید:


<!-- خط ۱۰ -->
  <h:outputText value="selectBooleanCheckbox" styleClass="info"/>
  <h:panelGroup>
    <h:outputText value="#{msg['form.selectBooleanCheckboxPrompt']}" styleClass="prompt" />
    <h:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/>
  </h:panelGroup>
  <h:outputText value="#{form.selectBooleanCheckbox}"/>

قالب تگ <h:selectBooleanCheckbox> در خط ۵ بالا در [Form.java] به شرح زیر است:


  private boolean selectBooleanCheckbox=true;

وقتی صفحه [index.xhtml] برای اولین بار درخواست می‌شود، صفحه‌ای که بازگردانده می‌شود به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند،
  • متن [2] توسط خط ۴ تولید می‌شود. چک‌باکس [3] توسط خط [5] تولید می‌شود. در اینجا، متد getSelectBooleanCheckbox از کلاس [Form.java] برای تعیین اینکه آیا باید تیک را فعال کرد یا خیر، استفاده شد. از آنجایی که این متد یک مقدار بولی true (درخت) برمی‌گرداند (به کد جاوا مراجعه کنید)، تیک فعال شد؛
  • خط ۷ کد XHTML، [4] را تولید می‌کند. بار دیگر، متد getSelectBooleanCheckbox از [Form.java] برای تولید متن [4] استفاده می‌شود.

جریان HTML تولیدشده توسط کد قبلی JSF به شرح زیر است:


<tr>
<td class="col1"><span class="info">selectBooleanCheckbox</span></td>
<td class="col2"><span class="prompt">mari&eacute;(e) : </span>
<input id="formulaire:selectBooleanCheckbox" type="checkbox" name="formulaire:selectBooleanCheckbox" checked="checked" /></td>
<td class="col3">true</td>
</tr>

در [4]، می‌توانیم تگ <input type="checkbox"> مربوط به HTML را که تولید شده است، مشاهده کنیم. مقدار true از قالب مربوطه باعث شد که ویژگی checked="checked" به تگ اضافه شود. این بدان معناست که چک‌باکس تیک خورده است.

اکنون، در زیر، تیک چک‌باکس [1] را برداریم، فرم [2] را ارسال کنیم و به نتیجه [3, 4] نگاه کنیم:

از آنجا که تیک چک‌باکس علامت‌زده نشده است، هیچ مقداری برای فیلد [1] وارد نشده است.

اعتبارسنجی فرم توسط [2] منجر به به‌روزرسانی قالب [Form.java] از طریق ورودی [1] شد. میدان selectBooleanCheckbox در [Form.java] سپس روی 'false' تنظیم شد. نمایش مجدد [index.xhtml] نشان می‌دهد که فیلد selectBooleanCheckbox در قالب واقعاً به [3] و [4] به‌روزرسانی شده است. شایان ذکر است که به لطف فیلد مخفی javax.faces.ViewState بود که JSF توانست تشخیص دهد که تیک‌باکسی که در ابتدا تیک خورده بود، توسط کاربر از تیک‌خوردگی خارج شده است. این به آن دلیل است که مقدار یک تیک‌باکس بدون تیک در میان مقادیری که توسط مرورگر ارسال می‌شود، گنجانده نمی‌شود. به لطف درخت کامپوننت ذخیره شده در فیلد مخفی javax.faces.ViewState، JSF تشخیص می‌دهد که یک تیک‌باکس با نام «selectBooleanCheckbox» در فرم وجود داشته و مقدار آن در مقادیر ارسال شده توسط مرورگر کلاینت گنجانده نشده است. بنابراین می‌تواند نتیجه بگیرد که این چک‌باکس در فرم ارسال‌شده علامت‌زده نشده بود، که این امر به آن اجازه می‌دهد تا مقدار بولین false را به مدل جاوا مرتبط اختصاص دهد:


  private boolean selectBooleanCheckbox;

2.5.20. برچسب <h:selectManyCheckBox>

تگ <h:selectManyCheckBox> یک گروه از کادرهای تیک ایجاد می‌کند و در نتیجه چندین تگ <input type="checkbox" ...> با مقدار HTML>. این تگ معادل تگ <h:selectManyListBox> است، با این تفاوت که موارد قابل انتخاب به جای یک لیست، به صورت چک‌باکس‌های مجاور ارائه می‌شوند. آنچه در مورد تگ <h:selectManyListBox> گفته شد، در اینجا نیز معتبر است.

کد JSF زیر را در نظر بگیرید:


          <!-- خط ۱۱ -->
          <h:outputText value="selectManyCheckbox" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" />
            <h: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"/>
            </h:selectManyCheckbox>
          </h:panelGroup>
<h:outputText value="#{form.selectManyCheckboxValue}"/>

قالب تگ <h:selectManyCheckbox> در خط ۵ بالا در [Form.java] به شرح زیر است:


private String[] selectManyCheckbox=new String[]{"1","3"};

وقتی صفحه [index.xhtml] برای اولین بار درخواست می‌شود، صفحه‌ای که بازگردانده می‌شود به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند،
  • متن [2] توسط خط ۴ تولید می‌شود. چک‌باکس‌های [3] توسط خطوط ۵–۱۰ تولید می‌شوند. برای هر یک از آن‌ها:
  • ویژگی itemLabel متنی را که در کنار کادر تیک نمایش داده می‌شود، تعریف می‌کند؛
  • ویژگی itemvalue مقداری را که در صورت تیک‌خورده بودن چک‌باکس به سرور ارسال می‌شود، تعریف می‌کند؛

مدل برای چهار چک‌باکس، فیلد Java زیر است:


private String[] selectManyCheckbox=new String[]{"1","3"};

این آرایه تعریف می‌کند:

  • که کدام چک‌باکس‌ها باید هنگام نمایش صفحه تیک خورده باشند. این موضوع توسط مقدار آن‌ها، c.a.d، و فیلد آن‌ها، itemValue، تعیین می‌شود. در مثال بالا، چک‌باکس‌هایی که مقادیرشان در آرایه {"1","3"} قرار دارد، تیک خورده خواهند شد. این همان چیزی است که در اسکرین‌شات بالا نشان داده شده است؛
  • هنگامی که صفحه ارسال می‌شود، قالب selectManyCheckbox آرایه مقادیر مربوط به چک‌باکس‌هایی را که کاربر تیک زده است، دریافت می‌کند. به زودی به این موضوع خواهیم پرداخت؛
  • خط ۱۲ از کد XHTML، [4] را تولید می‌کند. متد زیر getSelectManyCheckboxValue بود که [4] را تولید می‌کرد:

  public String getSelectManyCheckboxValue(){
    return getValue(getSelectManyCheckbox());
  }
  
  private String getValue(String[] chaines){
    String value="[";
    for(String chaine : chaines){
      value+=" "+chaine;
    }
    return value+"]";
}

جریان HTML تولیدشده توسط کد قبلی JSF به شرح زیر است:


    <tr>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:0" value="1" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:0"> rouge</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:1" value="2" type="checkbox" /><label for="formulaire:selectManyCheckbox:1"> bleu</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:2" value="3" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:2"> blanc</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:3" value="4" type="checkbox" /><label for="formulaire:selectManyCheckbox:3"> noir</label></td>
    </tr>
</table></td>
<td class="col3">[ 1 3]</td>
</tr>

چهار تگ HTML <input type="checkbox" ...> تولید شدند. تگ‌های خطوط ۳ و ۷ دارای ویژگی checked="checked" هستند که به این معنی است که آن‌ها با تیک ظاهر می‌شوند. توجه داشته باشید که همگی دارای ویژگی یکسان **name=&quot;form:selectManyCheckbox&quot;** هستند؛ به عبارت دیگر، چهار فیلد HTML نام یکسانی دارند. اگر کادرهای تیک‌دار در خطوط ۵ و ۹ توسط کاربر تیک خورده باشند، مرورگر مقادیر این چهار کادر را در قالب زیر ارسال خواهد کرد:

formulaire:selectManyCheckbox=2&formulaire:selectManyCheckbox=4

و قالب برای چهار چک‌باکس


private String[] selectManyCheckbox=new String[]{"1","3"};

آرایه {"2","4"} را دریافت خواهد کرد.

بیایید این را در زیر بررسی کنیم. در [1]، ما تغییر را اعمال می‌کنیم؛ در [2]، فرم را ارسال می‌کنیم. در [3]، نتیجهٔ به‌دست‌آمده این است:

مقادیر ارسال‌شده برای فیلدها در [1] به شرح زیر است:

formulaire%3AselectManyCheckbox=2&formulaire%3AselectManyCheckbox=4

2.5.21. برچسب <h:selectOneRadio>

تگ <h:selectOneRadio> یک گروه از دکمه‌های رادیویی غیرقابل‌تداخل را ایجاد می‌کند.

کد زیر را در نظر بگیرید JSF:


<!-- خط ۱۲ -->
          <h:outputText value="selectOneRadio" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneRadioPrompt']}" />
            <h: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"/>
            </h:selectOneRadio>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneRadio}"/>

قالب تگ <h:selectOneRadio> در خط ۵ بالا به صورت زیر در [Form.java] است:


  private String selectOneRadio="2";

وقتی صفحه [index.xhtml] برای اولین بار درخواست می‌شود، نمای حاصل به شرح زیر است:

  • خط ۲ از کد XHTML، [1] را تولید می‌کند،
  • متن [2] توسط خط ۴ تولید می‌شود. دکمه‌های رادیویی [3] توسط خطوط ۵ تا ۱۰ تولید می‌شوند. برای هر یک از آن‌ها:
  • ویژگی itemLabel متنی را که در کنار دکمه رادیویی نمایش داده می‌شود، تعریف می‌کند؛
  • ویژگی itemvalue مقداری را که در صورت انتخاب دکمه به سرور ارسال می‌شود، تعریف می‌کند؛

قالب برای چهار دکمهٔ رادیویی، فیلد Java زیر است:


  private String selectOneRadio="2";

این قالب تعریف می‌کند:

  • کدام دکمهٔ رادیویی باید هنگام نمایش صفحه انتخاب شود. این با مقدار آن، c.a.d، تعیین می‌شود. فیلد آن itemValue است. در مثال بالا، دکمه رادیویی با مقدار «2» انتخاب خواهد شد. این همان چیزی است که در اسکرین‌شات بالا نشان داده شده است؛
  • هنگامی که صفحه ارسال می‌شود، قالب selectOneRadio مقدار دکمه رادیویی انتخاب‌شده را دریافت می‌کند. به زودی به این موضوع خواهیم پرداخت؛
  • خط ۱۲ از کد XHTML، [4] را تولید می‌کند.

فید HTML تولیدشده توسط کد قبلی JSF به شرح زیر است:


<tr>
<td class="col1"><span class="info">selectOneRadio</span></td>
<td class="col2">moyen de transport pr&eacute;f&eacute;r&eacute; : <table id="formulaire:selectOneRadio">
    <tr>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:0" value="1" /><label for="formulaire:selectOneRadio:0"> voiture</label></td>
<td>
<input type="radio" checked="checked" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:1" value="2" /><label for="formulaire:selectOneRadio:1"> v&eacute;lo</label></td>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:2" value="3" /><label for="formulaire:selectOneRadio:2"> scooter</label></td>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:3" value="4" /><label for="formulaire:selectOneRadio:3"> marche</label></td>
</tr>

چهار تگ HTML <input type="radio" ...> تولید شده‌اند. تگ در خط ۸ دارای ویژگی checked="checked" است که به این معنی است که دکمه رادیویی متناظر با آن تیک خورده است. توجه داشته باشید که تمام تگ‌ها دارای ویژگی یکسان name=&quot;form:selectOneRadio&quot; هستند؛ به عبارت دیگر، چهار فیلد HTML نام یکسانی دارند. این شرط برای داشتن یک گروه از دکمه‌های رادیویی ناسازگار است: وقتی یکی انتخاب می‌شود، بقیه انتخاب نمی‌شوند.

در زیر، در [1]، یکی از دکمه‌های رادیویی انتخاب شده است؛ در [2]، فرم ارسال می‌شود؛ و در [3]، نتیجه نمایش داده می‌شود:

مقدار ارسال‌شده برای فیلد [1] به شرح زیر است:

formulaire%3AselectOneRadio=4

2.6. مثال mv-jsf2-04: لیست‌های پویا

2.6.1. برنامه

برنامه مانند قبل است:

تنها تغییرات مربوط به نحوه تولید موارد لیست برای فیلدهای [1] و [2] است. در اینجا، این موارد به‌صورت پویا توسط کد جاوا تولید می‌شوند، در حالی که در نسخه قبلی به‌صورت کد ثابت در صفحه JSF درج شده بودند.

2.6.2. پروژه NetBeans

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

پروژه [mv-jsf2-04] با پروژه [mv-jsf2-03] یکسان است، به جز تفاوت‌های زیر:

  • در [1]، در صفحه JSF، موارد لیست دیگر به صورت کد سخت درج نشده‌اند،
  • در [2]، قالب صفحه JSF [1] اصلاح خواهد شد،
  • در [3]، یکی از پیام‌ها اصلاح خواهد شد.

2.6.3. صفحه [index.xhtml] و قالب آن [Form.java]

صفحه JSF [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:f="http://java.sun.com/jsf/core">

  <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">
        <!-- زبان‌ها -->
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['form.titre']}"/></h1>
        <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
...
          <!-- خط ۴ -->
          <h:outputText value="selectOneListBox (size=1)" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/>
            <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItems value="#{form.selectOneListbox1Items}"/>
            </h:selectOneListbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneListBox1}"/>
          <!-- خط ۵ -->
          <h:outputText value="selectOneListBox (size=3)" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/>
            <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
              <f:selectItems value="#{form.selectOneListbox2Items}"/>
            </h:selectOneListbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneListBox2}"/>
          <!-- خط ۶ -->
          <h:outputText value="selectManyListBox (size=3)"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/>
            <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
              <f:selectItems value="#{form.selectManyListBoxItems}"/>
            </h:selectManyListbox>
            <p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyListBoxValue}"/>
          <!-- خط ۷ -->
          <h:outputText value="selectOneMenu" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneMenuPrompt']}"/>
            <h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
              <f:selectItems value="#{form.selectOneMenuItems}"/>
            </h:selectOneMenu>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneMenu}"/>
          <!-- خط ۸ -->
          <h:outputText value="selectManyMenu" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" />
            <h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
              <f:selectItems value="#{form.selectManyMenuItems}"/>
            </h:selectManyMenu>
            <p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>
...
          <!-- خط ۱۱ -->
          <h:outputText value="selectManyCheckbox" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" />
            <h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
              <f:selectItems value="#{form.selectManyCheckboxItems}"/>
            </h:selectManyCheckbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyCheckboxValue}"/>
          <!-- خط ۱۲ -->
          <h:outputText value="selectOneRadio" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneRadioPrompt']}" />
            <h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}">
              <f:selectItems value="#{form.selectOneRadioItems}"/>
            </h:selectOneRadio>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneRadio}"/>
        </h:panelGrid>
        <p>
          <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
        </p>
      </h:form>
    </h:body>
  </f:view>
</html>

تغییراتی که انجام شده در خطوط 26–28 نشان داده شده است. جایی که قبلاً کد به این صورت بود:


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItem itemValue="1" itemLabel="un"/>
              <f:selectItem itemValue="2" itemLabel="deux"/>
              <f:selectItem itemValue="3" itemLabel="trois"/>
</h:selectOneListbox>

اکنون این را داریم:


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>

سه تگ <f:selectItem> در خطوط ۲–۴ با یک تگ واحد <f:selectItems> در خط b جایگزین شده‌اند. این تگ دارای یک ویژگی value است که مقدار آن مجموعه‌ای از عناصر از نوع </span>javax.faces.model.SelectItem می‌باشد. در بالا، مقدار ویژگی </span>**value**<span style="color: #000000"> با فراخوانی متد زیر به دست می‌آید: [form].</span>**getSelectOneListbox1Items**:


  public SelectItem[] getSelectOneListbox1Items() {
    return getItems("A",3);
  }

  private SelectItem[] getItems(String label, int qte) {
    SelectItem[] items=new SelectItem[qte];
    for(int i=0;i<qte;i++){
      items[i]=new SelectItem(i,label+i);
    }
    return items;
}
  • در خط ۱، متد `getSelectOneListbox1Items` یک آرایه از عناصر از نوع `javax.faces.model.SelectItem` را بازمی‌گرداند که توسط متد خصوصی `getItems` در خط ۵ ساخته شده است. توجه باید داشت که متد getSelectOneListbox1Items، getter یک فیلد خصوصی selectOneListBox1Items نیست؛
  • کلاس javax.faces.model.SelectItem دارای سازندهای مختلفی است.

Image

ما از خط ۸ روش getItems، سازنده SelectItem(Object value, String label) استفاده می‌کنیم که با تگ JSF مطابقت دارد


    <f:selectItem itemValue="value" labelValue="label"/>
  • خطوط ۵–۱۰: متد getItems(String label, int qte) آرایه‌ای از qte عنصر از نوع SelectItem ایجاد می‌کند، که عنصر i-ام آن با سازنده SelectItem(i, label+i) ساخته می‌شود.

کد JSF


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>

بنابراین از نظر عملکردی معادل کد زیر JSF می‌شود:


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItem itemValue="0" itemLabel="A0"/>
              <f:selectItem itemValue="1" itemLabel="A1"/>
              <f:selectItem itemValue="2" itemLabel="A2"/>
</h:selectOneListbox>

همین امر در مورد سایر فهرست‌های صفحه JSF نیز صدق می‌کند. بنابراین قالب [Form.java] شامل روش‌های جدید زیر است:


  public SelectItem[] getSelectOneListbox1Items() {
    return getItems("A",3);
  }
  
  public SelectItem[] getSelectOneListbox2Items() {
    return getItems("B",4);
  }
  
  public SelectItem[] getSelectManyListBoxItems() {
    return getItems("C",5);
  }
  
  public SelectItem[] getSelectOneMenuItems() {
    return getItems("D",3);
  }
  
  public SelectItem[] getSelectManyMenuItems() {
   return getItems("E",4);
   }
  
  public SelectItem[] getSelectManyCheckboxItems() {
   return getItems("F",3);
   }
  
  public SelectItem[] getSelectOneRadioItems() {
   return getItems("G",4);
   }
  
  private SelectItem[] getItems(String label, int qte) {
    SelectItem[] items=new SelectItem[qte];
    for(int i=0;i<qte;i++){
      items[i]=new SelectItem(i,label+i);
    }
    return items;
}

2.6.4. فایل پیام

فقط یک پیام اصلاح شده است:

[messages_fr.properties]


form.titre=Java Server Faces - remplissage dynamique des listes

[messages_en.properties]


form.titre=Java Server Faces - dynamic filling of lists of elements

2.6.5. آزمایش‌ها

از خوانندگان دعوت می‌شود تا این نسخهٔ جدید را آزمایش کنند.

در اکثر موارد، عناصر پویا یک فرم نتیجه منطق کسب‌وکار هستند یا از یک پایگاه داده تأمین می‌شوند:

بیایید درخواست اولیه برای صفحه JSF [index.xhtml] توسط GET از مرورگر را بررسی کنیم:

  • صفحه JSF از طریق [1] درخواست می‌شود،
  • کنترل‌کننده [Faces Servlet] درخواست می‌کند که به صورت [3] نمایش داده شود. موتور JSF که صفحه را پردازش می‌کند، برای مثال، از مدل [Form.java] خود، مانند متد getSelectOneListBox1Items، فراخوانی می‌کند. این متد می‌تواند آرایه‌ای از عناصر از نوع SelectItem را بر اساس اطلاعات ذخیره‌شده در یک پایگاه داده بازگرداند. برای این کار، آن به لایه‌های [métier] و [2b] فراخوانی می‌کند.

2.7. مثال mv-jsf2-05: ناوبری – جلسه – مدیریت خطا

2.7.1. برنامه

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

  • در [1]، صفحه ۱ فرم – همچنین می‌توان از طریق لینک ۱ در [2] به آن دسترسی داشت
  • در [2]، یک گروه ۵ تایی از لینک‌ها.
  • به [3]، صفحه ۲ فرم که از طریق لینک ۲ در [2] قابل دسترسی است
  • در [4]، صفحه ۳ فرم که از طریق لینک ۳ در [2] قابل دسترسی است
  • به [5]، صفحه‌ای که از طریق لینک «ارسال یک استثنا» از [2] قابل دسترسی است
  • به [6]، صفحه‌ای که از طریق لینک ۴ در [2] دسترسی پیدا می‌شود. این صفحه خلاصه‌ای از ورودی‌های ثبت‌شده در صفحات ۱ تا ۳ را ارائه می‌دهد.

2.7.2. پروژه NetBeans

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

پروژه [mv-jsf2-05] دو ویژگی جدید را معرفی می‌کند:

  1. در [1]، صفحه JSF [index.xhtml] به سه صفحه، [form1.xhtml, form2.xhtml, form3.xhtml]، تقسیم شده است و ورودی‌ها در میان آن‌ها توزیع شده‌اند. صفحه [form4.xhtml] کپی‌ای از صفحه [index.xhtml] از پروژه قبلی است. در [2]، کلاس [Form.java] بدون تغییر باقی می‌ماند. این کلاس به عنوان قالبی برای چهار صفحه قبلی JSF عمل خواهد کرد،
  2. در [3]، صفحه‌ای به نام [exception.xhtml] اضافه می‌شود: این صفحه زمانی که استثنایی در برنامه رخ دهد، استفاده خواهد شد.

2.7.3. صفحات [form.xhtml] و قالب آن‌ها [Form.java]

2.7.3.1. کد صفحات XHTML

صفحه JSF [form1.xhtml] به شرح زیر است:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <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">
        <!--پیوندها -->
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['form1.titre']}"/></h1>
        <h:panelGrid columnClasses="col1,col2" columns="2" border="1">
          <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
          <!-- خط ۱ -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
          <!-- خط ۲ -->
          <h:outputText value="inputSecret"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.passwdPrompt']}"/>
            <h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
          </h:panelGroup>
          <!-- خط ۳ -->
          <h:outputText value="inputTextArea" styleClass="info"/>          
          <h:panelGroup>
            <h:outputText value="#{msg['form.descPrompt']}"/>
            <h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
          </h:panelGroup>         
        </h:panelGrid>
        <!-- پیوندها -->
        <h:panelGrid columns="6">
          <h:commandLink value="1" action="form1"/>
          <h:commandLink value="2" action="#{form.doAction2}"/>
          <h:commandLink value="3" action="form3"/>
          <h:commandLink value="4" action="#{form.doAction4}"/>
          <h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
          <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
        </h:panelGrid>
      </h:form>
      </h:body>
  </f:view>
</html>

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

لطفاً به نکات زیر توجه کنید:

  • خط ۱۶: جدولی که قبلاً سه ستون داشت، اکنون فقط دو ستون دارد. ستون ۳ که مقادیر مدل را نمایش می‌داد، حذف شده است. این مقادیر اکنون توسط [form4.xhtml] نمایش داده می‌شوند،
  • خطوط ۴۰–۴۶: جدولی شامل شش لینک. لینک‌های خطوط ۴۴ و ۴۶ دارای ناوبری ثابت هستند: ویژگی `action` آن‌ها به‌صورت سخت‌کد شده است. لینک‌های دیگر دارای ناوبری پویا هستند: ویژگی `action` آن‌ها به متدی در بیون فرم اشاره می‌کند که مسئول بازگرداندن کلید ناوبری است. متدهای مرجع در [Form.java] به شرح زیر هستند:

// رویدادها
  public String doAction2(){
    return "form2";
  }
  
  public String doAction4(){
    return "form4";
  }
  
  public String doAlea(){
    //یک عدد تصادفی بین ۱ و ۳
    int i=1+(int)(3*Math.random());
    // کلید ناوبری را بازگردانید
    return "form"+i;
  }
  
  public String throwException() throws java.lang.Exception{
    throw new Exception("Exception test");
}

فعلاً متد throwException در خط 17 را نادیده می‌گیریم. بعداً به آن باز خواهیم گشت. متدهای doAction2 و doAction4 به سادگی کلید ناوبری را بدون انجام هیچ پردازشی بازمی‌گردانند. بنابراین می‌توانستیم به همان آسانی بنویسیم:


<h:commandLink value="1" action="form1"/>
          <h:commandLink value="2" action="form2"/>
          <h:commandLink value="3" action="form3"/>
          <h:commandLink value="4" action="form4"/>
          <h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
          <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>

از سوی دیگر، متد doAlea یک کلید ناوبری تصادفی تولید می‌کند که مقدار آن از مجموعه {"form1", "form2", "form3"} گرفته می‌شود.

کد صفحات [form2.xhtml, form3.xhtml, form3.xhtml] مشابه کد صفحه [form1.xhtml] است.

2.7.3.2. دوره عمر قالب [Form.java] برای صفحات [form*.xhtml]

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

  • در [1]، صفحه ۱ را پر کنید و به صفحه ۳ بروید،
  • در [2]، صفحه ۳ پر می‌شود و ما به صفحه ۱ بازمی‌گردیم،
  • در [3]، صفحه ۱ دقیقاً همان‌طور که وارد شده بود نمایش داده می‌شود. سپس به صفحه ۳ بازمی‌گردیم،
  • در [4]، صفحه ۳ دقیقاً همان‌طور که وارد شده نمایش داده می‌شود.

مکانیزم فیلد مخفی [javax.faces.ViewState] برای توضیح این پدیده کافی نیست.

هنگام حرکت از [1] به [2]، چندین مرحله رخ می‌دهد:

  • قالب [Form.java] با POST از [form1.jsp] به‌روزرسانی می‌شود. به‌ویژه، فیلد inputText به مقدار «some other text» تنظیم می‌شود،
  • کلید ناوبری «form3» نمایش [form3.xhtml] را فعال می‌کند. ViewState که در [form3.xhtml] تعبیه شده است، تنها وضعیت اجزای موجود در [form3.xhtml] را منعکس می‌کند، نه اجزای موجود در [form1.xhtml].

هنگام سوئیچ از [2] به [3]:

  • مدل [Form.java] با POST از [form3.xhtml] به‌روزرسانی می‌شود. اگر قالب [Form.java] به پایان چرخه عمر خود رسیده باشد، یک شیء کاملاً جدید [Form.java] ایجاد می‌شود و سپس توسط POST از [form3.xhtml] به‌روزرسانی می‌شود. در این حالت، فیلد inputText در قالب به مقدار پیش‌فرض خود بازمی‌گردد:

  private String inputText="texte";

و آن را حفظ می‌کند: در واقع، در POST مشتق‌شده از [form3.xhtml]، هیچ چیزی فیلد inputText را که بخشی از قالب [form1.xhtml] است و نه [form3.xhtml]، به‌روزرسانی نمی‌کند،

  • کلید ناوبری "form1" باعث نمایش [form1.xhtml] می‌شود. صفحه قالب خود را نمایش می‌دهد. در مورد ما، فیلد ورودی login که به قالب inputText متصل است، texte را نمایش خواهد داد و نه مقدار «متن دیگری» که در [1] وارد شده است. برای اینکه فیلد inputText مقدار وارد شده در [1] را حفظ کند، باید دوره عمر قالب [Form.java] به جای «درخواست» روی «سشن» تنظیم شود. در این صورت،
    • پس از تکمیل POST برای [form1.xhtml]، قالب در جلسه (session) کلاینت قرار داده خواهد شد. فیلد inputText مقدار "another text" را خواهد داشت،
    • وقتی POST پس از [form3.xhtml] اجرا می‌شود، قالب از این جلسه بازیابی شده و توسط POST پس از [form3.xhtml] به‌روزرسانی خواهد شد. میدان inputText توسط این POST به‌روزرسانی نخواهد شد اما مقدار «some other text» را حفظ خواهد کرد set به دنبال اجرای POST از [form1.xhtml] و [1].

بنابراین اعلان bean [Form.java] به شرح زیر است:


package forms;

import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.model.SelectItem;

@ManagedBean
@SessionScoped
public class Form {

خط ۸ به بین دامنه جلسه می‌دهد.

2.7.4. مدیریت استثنا

بیایید معماری کلی یک برنامه JSF را مجدداً بررسی کنیم:

چه اتفاقی می‌افتد وقتی یک رویدادپرداز یا مدل استثناء‌ای را که از لایه کسب‌وکار سرچشمه می‌گیرد – مانند قطع اتصال غیرمنتظره از پایگاه داده – دریافت می‌کند؟

  • پردازشگرهای رویداد [2a] می‌توانند هر استثنایی را که از لایه [métier] منتشر می‌شود، رهگیری کرده و یک کلید ناوبری را به کنترل‌کننده [Faces Servlet] بازگردانند و آن را به یک صفحه خطای مخصوص به آن استثنا هدایت کنند،
  • برای قالب‌ها، این راه‌حل قابل استفاده نیست زیرا وقتی آن‌ها فراخوانی می‌شوند ([3,4])، سیستم در حال رندر کردن یک صفحهٔ مشخص (XHTML) است و دیگر در مرحلهٔ انتخاب آن صفحه نیست. چگونه می‌توانیم در حالی که در حال رندر یکی از صفحات هستیم، صفحات را تغییر دهیم؟ یک راه‌حل ساده، هرچند همیشه مناسب نیست، این است که استثنا را نادیده بگیرید، که در این صورت به سمت کانتینر سرولت در حال اجرای برنامه منتقل می‌شود. می‌توان برنامه را طوری پیکربندی کرد که هنگام انتقال یک استثنا به کانتینر سرولت، یک صفحهٔ خاص را نمایش دهد. این راه‌حل همیشه امکان‌پذیر است و اکنون آن را بررسی خواهیم کرد.

2.7.4.1. پیکربندی برنامه وب برای مدیریت خطا

یک برنامه وب در فایل [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.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>  
  <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/form1.xhtml</welcome-file>
  </welcome-file-list>
  <error-page>
    <error-code>500</error-code>
    <location>/faces/exception.xhtml</location>
  </error-page>
  <error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/faces/exception.xhtml</location>
  </error-page>
</web-app>

خطوط ۳۲–۳۹ حاوی تعریف دو صفحه خطا هستند. شما می‌توانید به هر تعداد که نیاز باشد تگ‌های <error-page> داشته باشید. تگ <location> صفحه‌ای را که در صورت بروز خطا باید نمایش داده شود، مشخص می‌کند. نوع خطای مرتبط با صفحه را می‌توان به دو روش تعریف کرد:

  • با استفاده از تگ <exception-type> که نوع جاوائی استثناء رسیدگی‌شده را تعریف می‌کند. بنابراین، تگ <error-page> در خطوط ۳۶–۳۹ مشخص می‌کند که اگر کانtejner servlet در حین اجرای برنامه، استثنایی از نوع [java.lang.Exception] یا یکی از انواع مشتق شده از آن (خط ۳۷) را بگیرد، باید صفحه [/faces/exception.xhtml] را نمایش دهد. (خط ۳۸). با استفاده از عمومی‌ترین نوع استثنا، [java.lang.Exception]، ما اطمینان حاصل می‌کنیم که همه استثناها مدیریت می‌شوند،
  • از طریق تگ <error-code> (خط ۳۳)، که یک کد خطای HTTP را تعریف می‌کند. به عنوان مثال، اگر یک مرورگر URL [http://machine:port/contexte/P] را درخواست کند و صفحه P در زمینه برنامه وجود نداشته باشد، برنامه در پاسخ مداخله نمی‌کند. این سرور سِروِلِت (servlet container) است که با ارسال یک صفحه خطای پیش‌فرض، این پاسخ را تولید می‌کند. خط اول پاسخ HTTP شامل کد خطای 404 است که نشان می‌دهد صفحه P درخواستی وجود ندارد. ممکن است بخواهید پاسخی تولید کنید که، برای مثال، با راهنمای سبک بصری برنامه مطابقت داشته باشد یا پیوندهایی برای حل مشکل ارائه دهد. در این صورت، از تگ <error-page> همراه با تگ <error-code>404</error-code> استفاده می‌کنید.

در بالا، کد خطای 500 HTTP کدی است که در صورت «از کار افتادن» (crash) برنامه بازگردانده می‌شود. این کدی است که در صورتی بازگردانده می‌شود که یک استثنا (exception) تا سرویس‌دهنده سرولت (servlet container) بالا برده (propagated) شود. بنابراین دو تگ <error-page> در خطوط ۲۸ تا ۳۵ احتمالاً اضافی هستند. ما هر دو را برای نشان دادن دو روش رسیدگی به خطا گنجانده‌ایم.

2.7.4.2. شبیه‌سازی استثنا

استثناء به‌طور مصنوعی توسط لینک [Lancer une exception] ایجاد می‌شود:

کلیک روی لینک [Lancer une exception] [1] باعث نمایش صفحه [2] می‌شود.

در کد صفحات [formx.xhtml]، لینک [Lancer une exception] به صورت زیر تولید می‌شود:


<!-- پیوندها -->
        <h:panelGrid columns="6">
          <h:commandLink value="1" action="form1"/>
...
          <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
        </h:panelGrid>

در خط ۵ می‌بینیم که وقتی لینک کلیک می‌شود، متد [form].throwException اجرا خواهد شد. این به شرح زیر است:


  public String throwException() throws java.lang.Exception{
    throw new Exception("Exception test");
}

یک استثنای از نوع [java.lang.Exception] در اینجا پرتاب می‌شود. این استثنا تا سرویس‌لت کانtejner propagate می‌شود و سپس صفحه [/faces/exception.xhtml] نمایش داده می‌شود.

2.7.4.3. اطلاعات مربوط به یک استثنا

وقتی یک استثنا تا کانتینر سرولت propagate می‌شود، کانتینر با ارسال اطلاعات مربوط به استثنا، صفحه خطای متناظر را نمایش می‌دهد. این اطلاعات به‌عنوان ویژگی‌های جدید درخواست در حال پردازش تنظیم می‌شوند. درخواست مرورگر و پاسخی که دریافت می‌کند، در اشیاء جاوا از نوع [HttpServletRequest request] و [HttpServletResponse response] محصور می‌شوند. این اشیاء در هر مرحله از پردازش درخواست مرورگر در دسترس هستند.

پس از دریافت درخواست HTTP از مرورگر، کانتینر سرولت آن را در شیء جاوا [HttpServletRequest request] جای می‌دهد و شیء [HttpServletResponse response] را ایجاد می‌کند که برای تولید پاسخ استفاده خواهد شد. این شیء به ویژه شامل کانال TCP-IP است که برای جریان پاسخ HTTP استفاده می‌شود. تمام لایه‌های t1، t2، …، tn که در پردازش شیء request درگیر هستند، به این دو شیء دسترسی دارند. هر یک از آن‌ها می‌تواند به عناصر درخواست اولیه request دسترسی داشته باشد و با غنی‌سازی شیء response پاسخ را آماده کند. برای مثال، یک لایه از localisation ممکن است با استفاده از متد response.setLocale(Locale l)، مقدار localisation را برای پاسخ تنظیم کند.

لایه‌های مختلف ti می‌توانند از طریق شیء request اطلاعات را بین خود منتقل کنند. این شیء دارای یک دیکشنری ویژگی‌ها است که هنگام ایجاد خالی است و می‌تواند توسط لایه‌های پردازش متوالی پر شود. این لایه‌ها می‌توانند اطلاعات مورد نیاز لایه پردازش بعدی را در ویژگی‌های شی request قرار دهند. دو روش برای مدیریت ویژگی‌های شی request وجود دارد:

  • `void setAttribute(String s, Object o)`, که یک شیء `o` را که با رشته `s` شناسایی می‌شود، به ویژگی‌ها اضافه می‌کند،
  • Object getAttribute(String s)، که ویژگی o را که با رشته s شناسایی شده است، بازیابی می‌کند.

هنگامی که یک استثنا تا سرویس‌گیرنده (servlet container) propagate می‌شود، سرویس‌گیرنده ویژگی‌های زیر را در درخواست در حال پردازش تنظیم می‌کند:

key
value
javax.servlet.error.status_code
کد خطای HTTP که به کلاینت بازگردانده خواهد شد
javax.servlet.error.exception
نوع جاوا از استثنا، به همراه پیام خطا.
javax.servlet.error.request_uri
متد URL که هنگام رخ دادن استثنا فراخوانی می‌شود
javax.servlet.error.servlet_name
سرولتی که هنگام رخ دادن استثنا در حال پردازش درخواست بود

ما از این ویژگی‌های درخواست در صفحه [exception.xhtml] برای نمایش آن‌ها استفاده خواهیم کرد.

2.7.4.4. صفحه خطا [exception.xhtml]

محتوای آن به شرح زیر است:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <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">
        <h3><h:outputText value="#{msg['exception.header']}"/></h3>
        <h:panelGrid columnClasses="col1,col2" columns="2" border="1">
          <h:outputText value="#{msg['exception.httpCode']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.status_code']}"/>
          <h:outputText value="#{msg['exception.message']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.exception']}"/>
          <h:outputText value="#{msg['exception.requestUri']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.request_uri']}"/>
          <h:outputText value="#{msg['exception.servletName']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.servlet_name']}"/>
        </h:panelGrid>
        <!-- پیوندها -->
        <h:panelGrid columns="6">
          <h:commandLink value="1" action="form1"/>
          <h:commandLink value="2" action="#{form.doAction2}"/>
          <h:commandLink value="3" action="form3"/>
          <h:commandLink value="4" action="#{form.doAction4}"/>
          <h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
        </h:panelGrid>
      </h:form>
    </h:body>
  </f:view>
</html>

2.7.4.4.1. بیان‌ها در صفحهٔ استثنا

در زنجیره پردازش درخواست مشتری، صفحه XHTML معمولاً آخرین حلقه در زنجیره است:

تمام عناصر در این زنجیره کلاس‌های جاوا هستند، از جمله صفحه XHTML. این در واقع توسط کانتینر سرولت، c.a.d، به یک سرولت و به یک کلاس استاندارد جاوا تبدیل می‌شود. به طور مشخص‌تر، صفحه XHTML به کُد جاوا تبدیل می‌شود که در داخل متد زیر اجرا می‌گردد:


public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {

JspFactory _jspxFactory = null;
PageContext pageContext = null;
HTTPSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
... 
...code de la page XHTML

از خط ۱۴ به بعد، کد جاوا مربوط به صفحه XHTML را خواهید یافت. این کد شامل تعدادی شیء خواهد بود که توسط متد _jspService، خط ۱ بالا، مقداردهی اولیه شده‌اند:

  • خط ۱: HttpServletRequest request: درخواستی که در حال پردازش است،
  • خط ۱: HttpServletResponse response: پاسخی که باید به کلاینت ارسال شود،
  • خط ۷: ServletContext application: ابجکتی که نماینده خود وب‌اپلیکیشن است. مانند شیء request، شیء application نیز ممکن است دارای ویژگی‌ها باشد. این ویژگی‌ها بین تمام درخواست‌های همه کلاینت‌ها مشترک هستند. آنها عموماً ویژگی‌های فقط-خواندنی هستند،
  • خط ۶: HTTPSession session: نمایانگر جلسهٔ مشتری است. مانند اشیاء request و application، شیء session ممکن است دارای ویژگی‌ها باشد. این ویژگی‌ها بین تمام درخواست‌های یک کلاینت مشترک هستند،
  • خط ۹: JspWriter out: یک جریان خروجی به مرورگر مشتری. این شیء برای اشکال‌زدایی یک صفحه XHTML مفید است. هر چیزی که از طریق out.println(text) نوشته شود، در مرورگر مشتری نمایش داده خواهد شد.

وقتی #{expression} در صفحه JSF نوشته می‌شود، expression می‌تواند کلید یکی از ویژگی‌های اشیاء request، session یا application که در بالا ذکر شدند باشد. ویژگی متناظر به ترتیب در این سه شیء جستجو می‌شود. بنابراین، #{key} به صورت زیر ارزیابی می‌شود:

  1. request.getAttribute(key)
  2. session.getAttribute(key)
  3. application.getAttribute(key)

به محض یافتن مقداری غیر از null، ارزیابی #{key} متوقف می‌شود. ممکن است بخواهید با مشخص کردن زمینه‌ای که ویژگی باید در آن جستجو شود، دقیق‌تر عمل کنید:

  • #{requestScope['clé']} برای جستجو در ویژگی در شیء درخواست،
  • #{sessionScope['clé']} برای جستجوی ویژگی در شیء جلسه،
  • #{applicationScope['clé']} برای جستجو در ویژگی در شیء برنامه.

این کاری است که در صفحه [exception.xhtml]، صفحه ۱۱۶ انجام شد. ویژگی‌های مورد استفاده به شرح زیر هستند:

کلید
دامنه
value
javax.servlet.error.status_code
درخواست
به بند 2.7.4.3 مراجعه کنید.
javax.servlet.error.exception
همان
همان
javax.servlet.error.request_uri
همان
همان
javax.servlet.error.servlet_name
همین
همان

پیام‌های مختلف مورد نیاز برای صفحات JSF و [exception.xhtml] به فایل‌های پیام موجود اضافه شده‌اند:

[messages_fr.properties]


exception.header=L'exception suivante s'est produite
exception.httpCode=Code HTTP de l'erreur
exception.message=Message de l'exception
exception.requestUri=URL demandée lors de l'erreur
exception.servletName=Nom de la servlet demandée lorsque l'erreur s'est produite

[messages_en.properties]


exception.header=The following error occurred
exception.httpCode=HTTP error code
exception.message=Exception message
exception.requestUri=URL requested when error occurred
exception.servletName=Servlet requested when error occurred

2.8. مثال mv-jsf2-06: اعتبارسنجی و تبدیل ورودی کاربر

2.8.1. برنامه

برنامه یک فرم ورودی نمایش می‌دهد. پس از اعتبارسنجی، همان فرم به همراه هرگونه پیام خطا در صورت نادرست بودن ورودی‌ها، به عنوان پاسخ بازگردانده می‌شود.

2.8.2. پروژه NetBeans

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

پروژه [mv-jsf2-06] بار دیگر بر اساس یک صفحه واحد، [index.html] [1]، و قالب آن، [Form.java] [2]، است. این پروژه همچنان از پیام‌های برداشته‌شده از [messages.properties] استفاده می‌کند، اما فقط به زبان فرانسوی ([3]). گزینه تغییر زبان در دسترس نیست.

2.8.3. محیط برنامه

در اینجا محتوای فایل‌هایی را که برنامه را پیکربندی می‌کنند، بدون ارائه توضیحات خاص ارائه می‌دهیم. این فایل‌ها به درک بهتر مطالب بعدی کمک می‌کنند.

[faces-config.xml]


<?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>
  </application>
</faces-config>

خط ۱۷ جدید است. بعداً توضیح داده خواهد شد.

فایل پیام [messages_fr.properties]


form.titre=Jsf - validations et conversions
saisie1.prompt=1-Nombre entier de type int
saisie2.prompt=2-Nombre entier de type int
saisie3.prompt=3-Nombre entier de type int
data.required=Vous devez entrer une donn\u00e9e
integer.required=Vous devez entrer un nombre entier
saisie4.prompt=4-Nombre entier de type int dans l'intervalle [1,10]
saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]
saisie5.prompt=5-Nombre r\u00e9el de type double
double.required=Vous devez entrer un nombre
saisie6.prompt=6-Nombre r\u00e9el>=0  de type double
saisie6.error=6-Vous devez entrer un nombre >=0
saisie7.prompt=7-Bool\u00e9en
saisie7.error=7-Vous devez entrer un bool\u00e9en
saisie8.prompt=8-Date au format jj/mm/aaaa
saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa
date.required=Vous devez entrer une date
saisie9.prompt=9-Cha\u00eene de 4 caract\u00e8res
saisie9.error=9-Vous devez entrer une cha\u00eene de 4 caract\u00e8res exactement
saisie9B.prompt=9B-Heure au format hh:mm
saisie9B.error=La cha\u00eene saisie ne respecte pas le format hh:mm
submit=Valider
cancel=Annuler
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du mod\u00e8le du formulaire
saisie10.prompt=10-Nombre entier de type int <1 ou >7
saisie10.incorrecte=10-Saisie n\u00b0 10 incorrecte
saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7
saisies11et12.incorrectes=La propri\u00e9t\u00e9 saisie11+saisie12=10 n'est pas v\u00e9rifi\u00e9e
saisies11et12.incorrectes_detail=La propri\u00e9t\u00e9 saisie11+saisie12=10 n'est pas v\u00e9rifi\u00e9e
saisie11.prompt=11-Nombre entier de type int
saisie12.prompt=12-Nombre entier de type int
error.sign="!"
error.sign_detail="!"

فایل سبک [styles.css] به شرح زیر است:


.info{
   font-family: Arial,Helvetica,sans-serif;
   font-size: 14px;
   font-weight: bold
}

.col1{
   background-color: #ccccff
}

.col2{
   background-color: #ffcccc
}

.col3{
   background-color: #ffcc66
}

.col4{
   background-color: #ccffcc
}

.error{
   color: #ff0000
}

.saisie{
   background-color: #ffcccc;
   border-color: #000000;
   border-width: 5px;
   color: #cc0033;
   font-family: cursive;
   font-size: 16px
}

.entete{
   font-family: 'Times New Roman',Times,serif;
   font-size: 14px;
   font-weight: bold
}

2.8.4. صفحه [index.xhtml] و قالب آن [Form.java]

صفحه [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:f="http://java.sun.com/jsf/core">
  <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');">
    <h2><h:outputText value="#{msg['form.titre']}"/></h2>
    <h:form id="formulaire">
      <h:messages globalOnly="true" />
      <h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
        <!--خط ۱ -->
        <h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/>
        <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
        <!-- خط ۲ -->
        <h:outputText value="#{msg['saisie1.prompt']}"/>
        <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
        <h:message for="saisie1" styleClass="error"/>
        <h:outputText value="#{form.saisie1}"/>
        <!-- خط ۳ -->
        <h:outputText value="#{msg['saisie2.prompt']}" />
        <h:inputText id="saisie2" value="#{form.saisie2}"  styleClass="saisie"/>
        <h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/>
        <h:outputText value="#{form.saisie2}"/>
        <!-- خط ۴ -->
        <h:outputText value="#{msg['saisie3.prompt']}" />
        <h:inputText id="saisie3" value="#{form.saisie3}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:message for="saisie3" styleClass="error"/>
        <h:outputText value="#{form.saisie3}"/>
        <!-- خط ۵ -->
        <h:outputText value="#{msg['saisie4.prompt']}" />
        <h:inputText id="saisie4" value="#{form.saisie4}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
          <f:validateLongRange minimum="1" maximum="10" />
        </h:inputText>
        <h:message for="saisie4" styleClass="error"/>
        <h:outputText value="#{form.saisie4}"/>
        <!-- خط ۶ -->
        ...
        <!-- خط ۷ -->
        ...
        <!-- خط ۸ -->
        ...
        <!-- خط ۹ -->
        ...
        <!-- خط ۱۰ -->
        ...
        <!-- خط ۱۱ -->
        ...
        <!-- خط 12 -->
        ...
        <!-- خط ۱۳ -->
        ...
      </h:panelGrid>
      <!--دکمه‌های کنترل -->
      <h:panelGrid columns="2">
        <h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
        <h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/>
      </h:panelGrid>
    </h:form>
  </h:body>
</html>

ویژگی اصلی جدید، درج تگ‌ها است:

  • برای نمایش پیام‌های خطا <h:messages> (خط 14)، <h:message> (خطوط 24، 29، 34)،
  • که محدودیت‌های اعتبارسنجی را بر ورودی کاربر اعمال می‌کنند <f:validateLongRange> (خط ۳۹)، <f:validateDoubleRange>، <f:validateLength<f:validateRegex>،
  • که یک مبدل بین ورودی و قالب آن را به صورت <f:convertDateTime> تعریف می‌کند.

قالب این صفحه کلاس زیر است، [Form.java]:


package forms;

import com.corejsf.util.Messages;
import java.util.Date;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;

@ManagedBean
@RequestScoped
public class Form {

public Form() {
}
// ورودی‌ها
private Integer saisie1 = 0;
private Integer saisie2 = 0;
private Integer saisie3 = 0;
private Integer saisie4 = 0;
private Double saisie5 = 0.0;
private Double saisie6 = 0.0;
private Boolean saisie7 = true;
private Date saisie8 = new Date();
private String saisie9 = "";
private Integer saisie10 = 0;
private Integer saisie11 = 0;
private Integer saisie12 = 0;
private String errorSaisie11 = "";
private String errorSaisie12 = "";

// اقدامات
public String submit() {
...
}

public String cancel() {
...
}

// اعتبارسنج‌ها
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
...
}
// گیرنده‌ها و تنظیم‌کننده‌ها
...
}

ویژگی جدید در اینجا این است که فیلدهای قالب دیگر صرفاً از نوع String نیستند، بلکه از انواع مختلفی هستند.

2.8.5. ورودی‌های مختلف در فرم

اکنون ورودی‌های مختلف فرم را به ترتیب بررسی خواهیم کرد.

2.8.5.1. ورودی‌های ۱ تا ۴: وارد کردن یک عدد صحیح

صفحه [index.xhtml] ورودی ۱ را به شکل زیر نمایش می‌دهد:


<!-- خط ۲ -->
        <h:outputText value="#{msg['saisie1.prompt']}"/>
        <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
        <h:message for="saisie1" styleClass="error"/>
        <h:outputText value="#{form.saisie1}"/>

قالب form.saisie1 در [Form.java] به شرح زیر تعریف شده است:


private Integer saisie1 = 0;

در یک GET در مرورگر، صفحه [index.xhtml] که با قالب آن [Form.java] مرتبط است، به صورت زیر رندر می‌شود:

  • خط ۲ تولید [1] می‌کند،
  • خط ۳ خروجی [2] را تولید می‌کند،
  • خط ۴ تولید [3] را می‌کند،
  • خط ۵ مقدار [4] را تولید می‌کند.

فرض کنید ورودی زیر ثبت و سپس تأیید می‌شود:

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

  • در [1]، ورودی نادرست،
  • در [2]، پیام خطایی که این موضوع را نشان می‌دهد،
  • در [3]، می‌توانیم ببینیم که مقدار فیلد «Integer saisie1» در مدل تغییر نکرده است.

بیایید توضیح دهیم چه اتفاقی افتاد. برای این کار، بیایید به چرخه پردازش صفحه JSF بازگردیم:

ما این چرخه را برای مؤلفه بررسی می‌کنیم:


<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>

و قالب آن:


private Integer saisie1 = 0;
  • در [A]، صفحه [index.xhtml] که در طول GET مرورگر ارسال شده بود، بازیابی می‌شود. در [A]، صفحه همان‌طور که کاربر دریافت کرده است، باقی می‌ماند. مؤلفه id="saisie1" به مقدار اولیه خود یعنی «0» بازمی‌گردد،
  • در [B]، مقادیر ارسال‌شده توسط مرورگر به اجزای صفحه اختصاص داده می‌شوند. در [B]، صفحه دقیقاً همان‌طور است که کاربر آن را وارد و ارسال کرده است. برای کامپوننت id="saisie1" مقدار ارسال‌شده «x» تعیین می‌شود،
  • در [C]، اگر صفحه حاوی اعتبارسنج‌ها و تبدیل‌کننده‌های صریح باشد، این موارد اجرا می‌شوند. تبدیل‌کننده‌های ضمنی نیز در صورتی اجرا می‌شوند که نوع فیلدی که با کامپوننت مرتبط است از نوع String نباشد. این مورد در اینجا صادق است، جایی که فیلد form.saisie1 از نوع Integer است. JSF تلاش خواهد کرد تا مقدار «x» از کامپوننت id="saisie1" را به نوع Integer تبدیل کند. این امر باعث خطایی خواهد شد که چرخه پردازش [A-F] را متوقف می‌کند. این خطا به کامپوننت id="saisie1" نسبت داده می‌شود. از طریق [D2]، فرآیند سپس مستقیماً به مرحله رندرینگ پاسخ منتقل می‌شود. همان صفحه، [index.xhtml]، بازگردانده می‌شود؛
  • مرحله [D] تنها در صورتی انجام می‌شود که تمام اجزای یک صفحه از مرحله تبدیل/اعتبارسنجی عبور کرده باشند. در همین مرحله است که مقدار جزء id="saisie1" به قالب آن، form.saisie1، اختصاص داده می‌شود.

اگر مرحله [C] با شکست مواجه شود، صفحه مجدداً نمایش داده می‌شود و کد زیر دوباره اجرا می‌گردد:


<!-- خط ۲ -->
        <h:outputText value="#{msg['saisie1.prompt']}"/>
        <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
        <h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>

پیامی که در [2] نمایش داده می‌شود از خط ۴ [index.xhtml] می‌آید. تگ <h:message for="idComposant"/> در صورت رخ دادن خطا، پیام خطای مربوط به کامپوننتی را که با ویژگی 'for' مشخص شده است، نمایش می‌دهد. پیامی که در [2] نمایش داده می‌شود استاندارد است و در فایل [javax/faces/Messages.properties] در آرشیو [jsf-api.jar] قرار دارد:

در [2] می‌بینیم که فایل پیام در چندین نسخه وجود دارد. بیایید محتویات [Messages_fr.properties] را بررسی کنیم:

...
# ==============================================================================
# خطاهای مؤلفه
# ==============================================================================
javax.faces.component.UIInput.CONVERSION={0} : une erreur de conversion est survenue.
javax.faces.component.UIInput.REQUIRED={0} : erreur de validation. Vous devez indiquer une valeur.
javax.faces.component.UIInput.UPDATE={0} : une erreur est survenue lors du traitement des informations que vous avez soumises. 
javax.faces.component.UISelectOne.INVALID={0} : erreur de validation. La valeur est incorrecte.
javax.faces.component.UISelectMany.INVALID={0} : erreur de validation. La valeur est incorrecte.

# ==============================================================================
# خطاهای مبدل
# ==============================================================================
...
javax.faces.converter.FloatConverter.FLOAT={2} : «{0 doit être un nombre composé dun ou de plusieurs chiffres.
javax.faces.converter.FloatConverter.FLOAT_detail={2} : «{0 doit être un nombre compris entre 1.4E-45 et 3.4028235E38. Exemple : {1}
javax.faces.converter.IntegerConverter.INTEGER={2} : «{0 doit être un nombre composé dun ou de plusieurs chiffres.
javax.faces.converter.IntegerConverter.INTEGER_detail={2} : «{0 doit être un nombre compris entre -2147483648 et 2147483647. Exemple : {1}
...


# ==============================================================================
# خطاهای اعتبارسنج
# ==============================================================================
javax.faces.validator.DoubleRangeValidator.MAXIMUM={1} : erreur de validation. La valeur est supérieure à la valeur maximale autorisée, "{0}".
javax.faces.validator.DoubleRangeValidator.MINIMUM={1} : erreur de validation. La valeur est inférieure à la valeur minimale autorisée, "{0}".
javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE={2} : erreur de validation. Lattribut spécifié nest pas compris entre les valeurs attendues {0} et {1}.
javax.faces.validator.DoubleRangeValidator.TYPE={0} : erreur de validation. La valeur nest pas du type correct.
...

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

  • خطاها در یک کامپوننت، خط ۳،
  • خطاهای تبدیل بین یک کامپوننت و مدل آن، خط ۱۲
  • خطاهای اعتبارسنجی در مواردی که اعتبارسنج‌ها در صفحه وجود دارند، خط ۲۳.

خطایی که در کامپوننت id="saisie1" رخ داده، یک خطای تبدیل از نوع String به نوع Integer است. پیام خطای مربوطه همان پیامی است که در خط ۱۸ فایل پیام‌ها آمده است.

javax.faces.converter.IntegerConverter.INTEGER_detail={2} : «{0}» doit être un nombre compris entre -2147483648 et 2147483647. Exemple : {1}

پیام خطایی که نمایش داده شد در زیر بازتولید شده است:

ما می‌توانیم این را در پیام مشاهده کنیم:

  • پارامتر {2} با شناسهٔ جزئی که خطای تبدیل برای آن رخ داده است، جایگزین شده است،
  • پارامتر {0} با ورودی ثبت‌شده در [1] برای این مؤلفه جایگزین شده است،
  • پارامتر {1} با عدد 9346 جایگزین شده است.

اکثر پیام‌های مرتبط با کامپوننت دو نسخه دارند: یک نسخه خلاصه و یک نسخه تفصیلی. این موضوع در خطوط ۱۶ تا ۱۸ صادق است:

javax.faces.converter.IntegerConverter.INTEGER={2} : «{0}» doit être un nombre composé d’un ou de plusieurs chiffres.
javax.faces.converter.IntegerConverter.INTEGER_detail={2} : «{0}» doit être un nombre compris entre -2147483648 et 2147483647. Exemple : {1}

پیامی با کلید _detail (خط ۲) همان پیام تفصیلی است. دیگری پیام خلاصه نامیده می‌شود. به‌طور پیش‌فرض، تگ <h:message> پیام تفصیلی را نمایش می‌دهد. این رفتار را می‌توان با استفاده از ویژگی‌های showSummary و showDetail تغییر داد. این کاری است که برای کامپوننت با شناسه 'saisie2' انجام شده است:


        <!-- خط ۳ -->
        <h:outputText value="#{msg['saisie2.prompt']}" />
        <h:inputText id="saisie2" value="#{form.saisie2}"  styleClass="saisie"/>
        <h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/>
<h:outputText value="#{form.saisie2}"/>

خط ۲: کامپوننت saisie2 به فیلد زیر، form.saisie2، متصل است:


  private Integer saisie2 = 0;

نتیجهٔ به‌دست‌آمده به شرح زیر است:

  • در [1]، پیام تفصیلی؛ در [2]، پیام خلاصه.

برچسب <h:messages> تمام پیام‌های خطای خلاصه‌شده از همهٔ اجزا را به‌صورت فهرست نمایش می‌دهد و همچنین پیام‌های خطایی را که به هیچ جزئی مربوط نیستند. در اینجا نیز ویژگی‌ها می‌توانند این رفتار پیش‌فرض را تغییر دهند:

  • showDetail: true / false برای مشخص کردن اینکه آیا پیام‌های تفصیلی نمایش داده شوند یا خیر،
  • showSummary: true / false برای مشخص کردن اینکه آیا پیام‌های خلاصه‌شده نمایش داده شوند یا خیر،
  • globalOnly: true / false برای مشخص کردن اینکه آیا فقط پیام‌های خطایی که به هیچ جزئی مرتبط نیستند نمایش داده شوند. چنین پیامی می‌تواند، برای مثال، توسط توسعه‌دهنده ایجاد شده باشد.

پیام خطای مرتبط با یک تبدیل را می‌توان به روش‌های مختلف تغییر داد. ابتدا می‌توان به برنامه دستور داد تا از یک فایل پیام متفاوت استفاده کند. این تغییر در [faces-config.xml]: انجام می‌شود:


<faces-config ...">
  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
    <message-bundle>messages</message-bundle>
  </application>
...
</faces-config>

خطوط ۳ تا ۸ یک فایل پیام را تعریف می‌کنند، اما این فایل توسط تگ‌های <h:message> و <h:messages> استفاده نمی‌شود. برای تعریف آن باید از تگ <message-bundle> در خط ۹ استفاده شود. خط ۹ به تگ‌های <h:message(s)> دستور می‌دهد که فایل [messages.properties] باید قبل از فایل [javax.faces.Messages.properties] پردازش شود. بنابراین، اگر خطوط زیر به فایل [messages_fr.properties] اضافه شوند:


#تبدیل‌ها
javax.faces.converter.IntegerConverter.INTEGER=erreur
javax.faces.converter.IntegerConverter.INTEGER_detail=erreur d\u00e9taill\u00e9e

خطای بازگردانده‌شده برای کامپوننت‌های saisie1 و saisie2 به صورت زیر درمی‌آید:

Image

راه دیگری برای تغییر پیام خطای تبدیل، استفاده از ویژگی converterMessage کامپوننت است، همانطور که در زیر برای کامپوننت saisie3 نشان داده شده است:


        <!-- خط ۴ -->
        <h:outputText value="#{msg['saisie3.prompt']}" />
        <h:inputText id="saisie3" value="#{form.saisie3}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:message for="saisie3" styleClass="error"/>
<h:outputText value="#{form.saisie3}"/>

کامپوننت saisie3 به فیلد زیر form.saisie3 متصل است:


  private Integer saisie3 = 0;
  • در خط ۳، ویژگی converterMessage صراحتاً پیامی را که در صورت بروز خطای تبدیل باید نمایش داده شود، تعیین می‌کند؛
  • خط ۳، ویژگی required="true" نشان می‌دهد که این فیلد اجباری است. این فیلد را نمی‌توان خالی گذاشت. یک فیلد زمانی خالی در نظر گرفته می‌شود که هیچ کاراکتری در آن نباشد یا حاوی دنباله‌ای از فضاها باشد. در اینجا نیز، یک پیام پیش‌فرض در [javax.faces.Messages.properties] وجود دارد:
javax.faces.component.UIInput.REQUIRED={0} : erreur de validation. Vous devez indiquer une valeur.

ویژگی requiredMessage به شما امکان می‌دهد این پیام پیش‌فرض را جایگزین کنید. اگر فایل [messages.properties] حاوی پیام‌های زیر باشد:


...
data.required=Vous devez entrer une donnée
integer.required=Vous devez entrer un nombre entier

نتیجه زیر قابل حصول است:

یا این یکی:

تأیید اینکه یک ورودی واقعاً یک عدد صحیح است همیشه کافی نیست. گاهی لازم است بررسی شود که عددی که وارد شده در یک بازهٔ مشخص قرار دارد. در چنین مواردی از یک اعتبارسنج استفاده می‌شود. ورودی شمارهٔ ۴ نمونه‌ای از این است. کد آن در [index.xhtml] به شرح زیر است:


        <!-- خط ۵ -->
        <h:outputText value="#{msg['saisie4.prompt']}" />
        <h:inputText id="saisie4" value="#{form.saisie4}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
          <f:validateLongRange minimum="1" maximum="10" />
        </h:inputText>
        <h:message for="saisie4" styleClass="error"/>
<h:outputText value="#{form.saisie4}"/>

در خط ۳، کامپوننت saisie4 به قالب زیر form.saisie4 متصل شده است:


  private Integer saisie4 = 0;

خطوط ۳–۵: تگ <h:inputText> دارای یک تگ فرزند <f:validateLongRange> است که دو ویژگی اختیاری minimum و maximum را می‌پذیرد. این تگ، که به نام اعتبارسنج نیز شناخته می‌شود، امکان اعمال یک محدودیت بر مقدار ورودی را فراهم می‌کند: مقدار ورودی نه تنها باید یک عدد صحیح باشد، بلکه اگر هر دو صفت minimum و maximum موجود باشند، باید عددی صحیح در بازه [minimum, maximum] باشد. بزرگ‌تر یا مساوی minimum اگر تنها ویژگی minimum موجود باشد، و کمتر یا مساوی maximum اگر تنها ویژگی maximum موجود باشد. اعتبارسنج <f:validateLongRange> دارای پیام‌های خطای پیش‌فرض در [javax.faces.Messages.properties] است:

1
2
3
javax.faces.validator.LongRangeValidator.MINIMUM={1} : erreur de validation. La valeur est inférieure à la valeur minimale autorisée, "{0}".
javax.faces.validator.LongRangeValidator.NOT_IN_RANGE={2} : erreur de validation. L’attribut spécifié n’est pas compris entre les valeurs attendues {0} et {1}.
javax.faces.validator.LongRangeValidator.TYPE={0} : erreur de validation. La valeur n’est pas du type correct.

بار دیگر، می‌توان این پیام‌ها را با پیام‌های دیگر جایگزین کرد. یک ویژگی به نام validatorMessage وجود دارد که به شما امکان می‌دهد یک پیام خاص برای کامپوننت تعریف کنید. بنابراین، با کد زیر JSF:


        <h:inputText id="saisie4" value="#{form.saisie4}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
          <f:validateLongRange minimum="1" maximum="10" />
</h:inputText>

و پیام زیر در [messages.properties]:


saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]

نتیجه زیر به دست می‌آید:

Image

2.8.5.2. ورودی‌های ۵ و ۶: وارد کردن یک عدد حقیقی

قوانین وارد کردن اعداد حقیقی مشابه قوانین وارد کردن اعداد صحیح است. کد XHTML برای ورودی‌های ۵ و ۶ به شرح زیر است:


<!-- خط ۶ -->
        <h:outputText value="#{msg['saisie5.prompt']}" />
        <h:inputText id="saisie5" value="#{form.saisie5}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/>
        <h:message for="saisie5" styleClass="error"/>
        <h:outputText value="#{form.saisie5}"/>
        <!-- خط ۷ -->
        <h:outputText value="#{msg['saisie6.prompt']}"/>
        <h:inputText id="saisie6" value="#{form.saisie6}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}" validatorMessage="#{msg['saisie6.error']}">
          <f:validateDoubleRange minimum="0.0"/>
        </h:inputText>
        <h:message for="saisie6" styleClass="error"/>
        <h:outputText value="#{form.saisie6}"/>

عناصر قالب [Form.java] که به اجزای saisie5 و saisie6 متصل هستند:


  private Double saisie5 = 0.0;
  private Double saisie6 = 0.0;

پیام‌های خطا مرتبط با مبدل‌ها و اعتبارسنج‌ها برای اجزای saisie5 و saisie6, در [messages.properties]:


double.required=Vous devez entrer un nombre
saisie6.error=6-Vous devez entrer un nombre >=0

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

Image

2.8.5.3. ورودی ۷: وارد کردن یک مقدار بولی

یک مقدار بولین معمولاً باید با استفاده از یک تیک‌باکس وارد شود. اگر از طریق یک فیلد ورودی وارد شود، رشته «true» به مقدار بولین true و هر رشته دیگری به مقدار بولین false تبدیل می‌شود.

کد XHTML از مثال:


<!-- خط ۸ -->
        <h:outputText value="#{msg['saisie7.prompt']}"/>
        <h:inputText id="saisie7" value="#{form.saisie7}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/>
        <h:message for="saisie7" styleClass="error"/>
        <h:outputText value="#{form.saisie7}"/>

قالب برای کامپوننت saisie7:


  private Boolean saisie7 = true;

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

در [1]، مقدار وارد شده است. پس از تبدیل، این رشته «x» به مقدار بولی false تبدیل می‌شود. این موضوع توسط [2] نشان داده شده است. مقدار [3] در قالب تغییر نکرده است. این مقدار تنها پس از موفقیت‌آمیز بودن تمام تبدیل‌ها و اعتبارسنجی‌های صفحه تغییر می‌کند. در این مثال چنین نبود.

2.8.5.4. ورودی ۸: وارد کردن تاریخ

در مثال، یک تاریخ با استفاده از کد زیر وارد می‌شود: XHTML:


<!-- خط ۹ -->
        <h:outputText value="#{msg['saisie8.prompt']}"/>
        <h:inputText id="saisie8" value="#{form.saisie8}"  styleClass="saisie" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}">
          <f:convertDateTime pattern="dd/MM/yyyy"/>
        </h:inputText>
        <h:message for="saisie8" styleClass="error"/>
        <h:outputText value="#{form.saisie8}">
          <f:convertDateTime pattern="dd/MM/yyyy"/>
        </h:outputText>

کامپوننت saisie8 در خط ۳ از یک مبدل java.lang.String <--> java.util.Date استفاده می‌کند. قالب form.saisie8 مرتبط با کامپوننت saisie8 به شرح زیر است:


  private Date saisie8 = new Date();

کامپوننت تعریف‌شده در خطوط ۷–۹ نیز از یک مبدل استفاده می‌کند، اما فقط در جهت java.util.Date --> java.lang.String.

مبدل <f:convertDateTime> از ویژگی‌های مختلفی از جمله ویژگی «pattern» پشتیبانی می‌کند که فرمت رشتهٔ کاراکتری را برای تبدیل به تاریخ یا فرمت نمایش تاریخ مشخص می‌کند.

در اولین درخواست برای صفحه [index.xhtml]، خط ۸ پیشین به صورت زیر نمایش داده می‌شود:

فیلدهای [1] و [2] هر دو مقدار قالب form.saisie8 را نمایش می‌دهند:


  private Date saisie8 = new Date();

که در آن saisie8 روی تاریخ امروز تنظیم شده است. مبدلی که در هر دو مورد برای نمایش تاریخ استفاده می‌شود به شرح زیر است:


            <f:convertDateTime pattern="dd/MM/yyyy"/>

که در آن dd (روز) نشان‌دهنده شماره روز، MM (ماه) نشان‌دهنده شماره ماه و yyyy (سال) نشان‌دهنده سال است. در [1]، از مبدل برای تبدیل معکوس java.lang.String --> java.util.Date استفاده می‌شود. بنابراین تاریخ وارد شده باید برای معتبر بودن از قالب «dd/MM/yyyy» پیروی کند.

در [javax.faces.Messages.properties] پیام‌های پیش‌فرض برای تاریخ‌های نامعتبر وجود دارد:

javax.faces.converter.DateTimeConverter.DATE={2} : «{0}» n’a pas pu être interprété en tant que date.
javax.faces.converter.DateTimeConverter.DATE_detail={2} : «{0}» n’a pas pu être interprété en tant que date. Exemple : {1} 

که می‌توانید آن‌ها را با پیام‌های دلخواه خود جایگزین کنید. برای مثال:


<h:inputText id="saisie8" value="#{form.saisie8}"  styleClass="saisie" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}">
  <f:convertDateTime pattern="dd/MM/yyyy"/>
</h:inputText>

پیامی که در صورت بروز خطای تبدیل نمایش داده می‌شود، پیام زیر با کلید saisie8.error خواهد بود:


saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa

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

Image

2.8.5.5. ورودی ۹: وارد کردن یک رشته با طول مشخص

ورودی ۹ نشان می‌دهد چگونه یک رشتهٔ واردشده را به تعداد مشخصی از کاراکترها در یک بازه محدود کنیم:


<!-- خط ۱۰ -->
        <h:outputText value="#{msg['saisie9.prompt']}"/>
        <h:inputText id="saisie9" value="#{form.saisie9}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9.error']}">
          <f:validateLength minimum="4" maximum="4"/>
        </h:inputText>
        <h:message for="saisie9" styleClass="error"/>
        <h:outputText value="#{form.saisie9}"/>

خط ۴: اعتبارسنج <f:validateLength minimum="4" maximum="4"/> نیاز دارد که رشتهٔ واردشده دقیقاً ۴ کاراکتر داشته باشد. تنها یکی از ویژگی‌ها قابل استفاده است: 'minimum' برای حداقل تعداد کاراکترها یا 'maximum' برای حداکثر تعداد.

قالب form.saisie9 برای مؤلفه saisie9 در خط ۳ به شرح زیر است:


  private String saisie9 = "";

برای این نوع اعتبارسنجی، پیام‌های خطای پیش‌فرض وجود دارند:

javax.faces.validator.LengthValidator.MAXIMUM={1} : erreur de validation. La longueur est supérieure à la valeur maximale autorisée, "{0}".
javax.faces.validator.LengthValidator.MINIMUM={1} : erreur de validation. La longueur est inférieure à la valeur minimale autorisée, "{0}".

که می‌توان آن را با استفاده از ویژگی validatorMessage همان‌طور که در خط ۳ بالا نشان داده شده است، بازنویسی کرد. پیام کلیدی برای saisie9.error به شرح زیر است:


saisie9.error=9-Vous devez entrer une chaîne de 4 caractères exactement

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

Image

2.8.5.6. ورودی 9B: وارد کردن یک رشته که باید با یک قالب مطابقت داشته باشد

ورودی 9B نشان می‌دهد چگونه یک رشتهٔ واردشده را به تعداد کاراکترهای مشخصی در یک بازه محدود کرد:


<!-- خط 10B -->
        <h:outputText value="#{msg['saisie9B.prompt']}"/>
        <h:inputText id="saisie9B" value="#{form.saisie9B}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9B.error']}">
          <f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/>
        </h:inputText>
        <h:message for="saisie9B" styleClass="error"/>
        <h:outputText value="#{form.saisie9B}"/>

در خط ۴، اعتبارسنج <f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/> نیاز دارد که رشته وارد شده با الگوی یک عبارت منظم مطابقت داشته باشد، در این مورد: یک دنباله از ۰ یا چند فاصله، ۲ رقم، دو نقطه (:)، ۲ رقم، یک دنباله از ۰ یا چند فاصله.

الگوی form.saisie9B برای مؤلفه saisie9B در خط ۳ به شرح زیر است:


private String saisie9B;

برای این نوع اعتبارسنجی، پیام‌های خطای پیش‌فرض وجود دارند:

1
2
3
4
5
6
javax.faces.validator.RegexValidator.PATTERN_NOT_SET=Le modèle d’expression régulière doit être défini.
javax.faces.validator.RegexValidator.PATTERN_NOT_SET_detail=La valeur définie du modèle d’expression régulière ne peut pas être vide.
javax.faces.validator.RegexValidator.NOT_MATCHED=Discordance du modèle d’expression régulière.
javax.faces.validator.RegexValidator.NOT_MATCHED_detail=Discordance du modèle d’expression régulière «{0}».
javax.faces.validator.RegexValidator.MATCH_EXCEPTION=Erreur dans l’expression régulière.
javax.faces.validator.RegexValidator.MATCH_EXCEPTION_detail=Erreur dans l’expression régulière,  «{0}»

که می‌توان آن را با استفاده از ویژگی validatorMessage همان‌طور که در خط ۳ بالا نشان داده شده است، بازنویسی کرد. پیام کلیدی saisie9.error به شرح زیر است:


saisie9B.error=La cha\u00eene saisie ne respecte pas le format hh:mm

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

Image

2.8.5.7. تمرین ۱۰: یک روش اعتبارسنجی خاص بنویسید

خلاصه اینکه: JSF به شما امکان می‌دهد تا در میان مقادیر وارد شده، اعتبار اعداد (اعداد صحیح، اعداد اعشاری)، تاریخ‌ها، طول رشته‌ها و اینکه آیا یک مقدار با یک عبارت منظم مطابقت دارد یا خیر را بررسی کنید. JSF به شما امکان می‌دهد تا اعتبارسنج‌ها و مبدل‌های خود را به موارد موجود اضافه کنید. این موضوع در اینجا پوشش داده نشده است، اما می‌توانید برای جزئیات بیشتر به [ref2] مراجعه کنید.

در اینجا روش دیگری را ارائه می‌دهیم: روشی که شامل اعتبارسنجی داده‌های وارد شده با استفاده از متدی از مدل فرم است. مثال زیر:


<!-- خط ۱۱ -->
        <h:outputText value="#{msg['saisie10.prompt']}"/>
        <h:inputText id="saisie10" value="#{form.saisie10}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
        <h:message for="saisie10" styleClass="error"/>
        <h:outputText value="#{form.saisie10}"/>

مدل form.saisie10 که با کامپوننت saisie10 در خط ۳ مرتبط است، به شرح زیر است:


  private Integer saisie10 = 0;

ما می‌خواهیم عدد وارد شده <1 یا >7 باشد. این مورد را نمی‌توان با استفاده از اعتبارسنج‌های پایه JSF بررسی کرد. بنابراین ما روش اعتبارسنجی خود را برای کامپوننت saisie10 می‌نویسیم. این را با استفاده از ویژگی validator کامپوننتی که باید اعتبارسنجی شود، مشخص می‌کنیم:


          <h:inputText id="saisie10" value="#{form.saisie10}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>

کامپوننت saisie10 توسط متد form.validateSaisie10 اعتبارسنجی می‌شود. این متد به شرح زیر است:


  public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
    int saisie = (Integer) value;
    if (!(saisie < 1 || saisie > 7)) {
      FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      throw new ValidatorException(message);
    }
}

امضای یک متد اعتبارسنجی باید همان چیزی باشد که در خط ۱ نشان داده شده است:

  • FacesContext زمینه: زمینه اجرای صفحه – دسترسی به اطلاعات مختلف، از جمله اشیاء درخواست HttpServletRequest و پاسخ HttpServletResponse را فراهم می‌کند،
  • UIComponent component: کامپوننتی که باید اعتبارسنجی شود. تگ <h:inputText> توسط یک کامپوننت از نوع UIInput که از UIComponent مشتق شده است، نمایش داده می‌شود. در اینجا، این کامپوننت UIInput است که به عنوان پارامتر دوم دریافت می‌شود،
  • مقدار شیء: مقداری که برای اعتبارسنجی وارد شده و به نوع داده مدل تبدیل شده است. مهم است که در اینجا درک کنیم اگر تبدیل از String به نوع داده مدل ناموفق باشد، متد اعتبارسنجی اجرا نمی‌شود. وقتی متد validateSaisie10 فراخوانی می‌شود، یعنی تبدیل از String به Integer با موفقیت انجام شده است. بنابراین پارامتر سوم از نوع Integer است.
  • خط ۲: مقدار وارد شده به نوع int, تبدیل می‌شود
  • خط ۳: یک بررسی انجام می‌شود تا اطمینان حاصل شود که مقدار وارد شده <1 یا >7 است. اگر اینطور باشد، اعتبارسنجی کامل می‌شود. در غیر این صورت، اعتبارسنج باید خطا را با پرتاب یک استثنا از نوع ValidatorException گزارش دهد.

کلاس ValidatorException دارای دو سازنده است:

  • سازنده [1] یک پیام خطا از نوع FacesMessage را به عنوان پارامتر می‌پذیرد. این نوع پیام، همان پیامی است که توسط تگ‌های <h:messages> و <h:message> نمایش داده می‌شود،
  • سازنده [2] همچنین امکان دربرگرفتن علت از نوع Throwable یا مشتقی از خطا را فراهم می‌کند.

ما باید یک پیام از نوع FacesMessage بسازیم. این کلاس سازندهای مختلفی دارد:

سازنده [1] ویژگی‌های یک شیء FacesMessage را تعریف می‌کند:

  • FacesMessage.Severity شدت: یک سطح شدت که از شمارش‌پذیر زیر گرفته شده است: SEVERITY_ERROR, SEVERITY_FATAL, SEVERITY_INFO, SEVERITY_WARN,
  • خلاصهٔ رشته: نسخهٔ خلاصهٔ پیام خطا – با استفاده از تگ‌های <h:message showSummary="true"> و <h:messages> نمایش داده می‌شود،
  • رشته جزئیات: نسخهٔ تفصیلی پیام خطا – با استفاده از برچسب‌های <h:message> و <h:messages showDetail="true"> نمایش داده می‌شود.

هر یک از سازندها قابل استفاده است؛ پارامترهای از دست رفته را می‌توان بعداً با استفاده از متدهای set تنظیم کرد.

سازنده [1] به شما اجازه نمی‌دهد پیامی را از یک فایل پیام بین‌المللی مشخص کنید. این، البته، مایه تأسف است. دیوید گِری و کای هورستمن این کمبود را در کتابشان *Core JavaServer Faces* با کلاس ابزار com.corejsf.util.Messages برطرف می‌کنند. این کلاس است که در خط ۴ کد جاوا برای ایجاد پیام خطا استفاده می‌شود. این کلاس تنها شامل متدهای استاتیک است، از جمله متد getMessage که در خط ۴ به کار رفته است:


   public static FacesMessage getMessage(String bundleName, String resourceId, Object[] params)

متد getMessage سه پارامتر می‌گیرد:

  • String bundleName: نام یک فایل پیام بدون پسوند .properties اما شامل نام پکیج آن. در اینجا، پارامتر اول ما می‌تواند messages باشد تا به فایل [messages.properties] اشاره کند. قبل از استفاده از فایلی که توسط پارامتر اول مشخص شده است، getMessage تلاش می‌کند از فایل پیام برنامه استفاده کند، در صورتی که چنین فایلی وجود داشته باشد. بنابراین، اگر یک فایل پیام در [faces-config.xml] با استفاده از تگ زیر اعلام شده باشد:

  <application>
...
    <message-bundle>messages</message-bundle>
</application>

در این صورت می‌توان null را به عنوان پارامتر اول به متد getMessage ارسال کرد. این کاری است که در اینجا انجام شده است (به [web.xm]، صفحه ۱۲۰ مراجعه کنید)،

  • رشته resourceId: کلید برای پیامی که باید در فایل پیام پردازش شود. ما دیده‌ایم که یک پیام می‌تواند هم نسخه خلاصه و هم نسخه تفصیلی داشته باشد. resourceId شناسه نسخه خلاصه است. نسخهٔ تفصیلی به‌طور خودکار با استفاده از کلید resourceId_detail بازیابی خواهد شد. بنابراین، ما دو پیام در [messages.properties] برای خطای مربوط به ورودی شمارهٔ ۱۰ خواهیم داشت:

saisie10.incorrecte=10-Saisie  10 incorrecte
saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7

پیامی با نوع FacesMessage که توسط متد Messages.getMessage تولید می‌شود، شامل هر دو نسخه خلاصه و تفصیلی است، در صورتی که یافت شده باشند. هر دو نسخه باید موجود باشند؛ در غیر این صورت، یک استثنا با نوع [NullPointerException] پرتاب می‌شود،

  • Object[] params: پارامترهای واقعی پیام در صورتی که پارامترهای رسمی {0}, {1}, ... داشته باشد. این پارامترهای رسمی با عناصر آرایه params جایگزین می‌شوند.

بیایید به کد مربوط به متد اعتبارسنجی کامپوننت saisie10 بازگردیم:


  public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
    int saisie = (Integer) value;
    if (!(saisie < 1 || saisie > 7)) {
      FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      throw new ValidatorException(message);
    }
}
  • در [4]، پیام از نوع FacesMessage با استفاده از متد استاتیک Messages.getMessage ایجاد می‌شود،
  • در [5]، سطح جدیت پیام تنظیم می‌شود،
  • در [6]، یک استثنا از نوع ValidatorException با پیامی که قبلاً ساخته شده بود، پرتاب می‌شود. متد اعتبارسنجی توسط کد زیر XHTML فراخوانی شد:

<!-- خط ۱۱ -->
        <h:outputText value="#{msg['saisie10.prompt']}"/>
        <h:inputText id="saisie10" value="#{form.saisie10}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
        <h:message for="saisie10" styleClass="error"/>
<h:outputText value="#{form.saisie10}"/>

در خط ۳، متد اعتبارسنجی برای کامپوننتی با شناسه saisie10 اجرا می‌شود. در نتیجه، پیام خطایی که توسط متد validateSaisie10 تولید شده است، به این کامپوننت مرتبط می‌شود و بنابراین در خط ۴ نمایش داده می‌شود. (ویژگی for="saisie10"). این نسخهٔ تفصیلی است که به‌طور پیش‌فرض توسط تگ <h:message> نمایش داده می‌شود.

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

Image

2.8.5.8. ورودی‌های ۱۱ و ۱۲: اعتبارسنجی یک گروه از مؤلفه‌ها

تا کنون، روش‌های اعتبارسنجی که با آن‌ها مواجه شده‌ایم تنها یک مؤلفه را اعتبارسنجی کرده‌اند. اگر اعتبارسنجی مورد نیاز شامل چندین مؤلفه باشد چه؟ این چیزی است که اکنون به آن خواهیم پرداخت. در فرم:

Image

ما می‌خواهیم ورودی‌های ۱۱ و ۱۲ دو عدد صحیح باشند که مجموع آن‌ها برابر با ۱۰ باشد.

کد JSF به صورت زیر خواهد بود:


<!-- خط 12 -->
        <h:outputText value="#{msg['saisie11.prompt']}"/>
        <h:inputText id="saisie11" value="#{form.saisie11}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:panelGroup>
          <h:message for="saisie11" styleClass="error"/>
          <h:outputText value="#{form.errorSaisie11}" styleClass="error"/>
        </h:panelGroup>
        <h:outputText value="#{form.saisie11}"/>
        <!-- خط ۱۳ -->
        <h:outputText value="#{msg['saisie12.prompt']}"/>
        <h:inputText id="saisie12" value="#{form.saisie12}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:panelGroup>
          <h:message for="saisie12" styleClass="error"/>
          <h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
        </h:panelGroup>
        <h:outputText value="#{form.saisie12}"/>

و قالب مرتبط:


  private Integer saisie11 = 0;
  private Integer saisie12 = 0;
  private String errorSaisie11 = "";
private String errorSaisie12 = "";

در خط ۳ کد JSF، از تکنیک‌های توصیف‌شده پیشین برای بررسی اینکه مقداری که برای مؤلفه saisie11 وارد شده، واقعاً یک عدد صحیح است، استفاده می‌کنیم. همین امر در خط ۱۱ برای مؤلفه saisie12 نیز صدق می‌کند. برای بررسی اینکه saisie11 + saisie12 = 10، می‌توانیم یک اعتبارسنج اختصاصی بسازیم. این راه‌حل ترجیحی است. بار دیگر، برای کشف آن، [ref2] را بررسی خواهیم کرد. اما در اینجا رویکرد متفاوتی در پیش می‌گیریم.

صفحه [index.xhtml] توسط دکمه‌ای به نام [Valider] اعتبارسنجی می‌شود که کد آن JSF به شرح زیر است:


<!-- دکمه‌های کنترل -->
      <h:panelGrid columns="2">
        <h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
        ...
      </h:panelGrid>

که پیام msg['submit'] به شرح زیر است:


submit=Valider

همان‌طور که در خط ۳ مشاهده می‌شود، متد form.submit برای رسیدگی به کلیک روی دکمه [Valider] اجرا خواهد شد. این به شرح زیر است:


  // اقدامات
  public String submit() {
    // آخرین ارسال‌ها
    validateForm();
    // همان فرم بازگردانده می‌شود
    return null;
  }

  // اعتبارسنجی‌های جهانی
  private void validateForm() {
    if ((saisie11 + saisie12) != 10) {
...
}

مهم است که درک کنیم وقتی متد submit اجرا می‌شود:

  • تمام اعتبارسنج‌ها و تبدیل‌کننده‌های فرم اجرا شده و با موفقیت به پایان رسیده‌اند،
  • میدان‌های مدل [Form.java] با مقادیری که توسط کلاینت ارسال شده، پر شده‌اند.

در واقع، بیایید به چرخه پردازش POST JSF بازگردیم:

متد submit یک رویدادپرداز است. این رویداد clic را روی دکمه [Valider] مدیریت می‌کند. مانند همهٔ رسیدگی‌کننده‌های رویداد، این متد در فاز [E] اجرا می‌شود، پس از آنکه همهٔ اعتبارسنج‌ها و تبدیل‌کننده‌ها با موفقیت اجرا شده باشند ([C]) و مدل با مقادیر ارسال‌شده به‌روزرسانی شده باشد ([D]). بنابراین دیگر نیازی به پرتاب استثناهای نوع [ValidatorException]، مانند گذشته، نداریم. ما به سادگی فرم را همراه با پیام‌های خطا بازمی‌گردانیم:

در [1]، کاربر را هشدار می‌دهیم و در [2] و [3]، یک نشانگر خطا نمایش می‌دهیم. در کد JSF، پیام [1] به صورت زیر تولید خواهد شد:


<h:form id="formulaire">
      <h:messages globalOnly="true" />
      <h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
        <!-- خط ۱ -->
        ...

در خط ۲، تگ <h:messages> به طور پیش‌فرض خلاصه‌ای از پیام‌های خطا برای تمام ورودی‌های نادرست در اجزای فرم و همچنین تمام پیام‌های خطایی را که به هیچ جزئی مرتبط نیستند، نمایش می‌دهد. ویژگی globalOnly="true" نمایش را به مورد دوم محدود می‌کند.

پیام‌های [2] و [3] با استفاده از تگ‌های ساده <h:outputText> نمایش داده می‌شوند:


<!-- خط ۱۲ -->
        <h:outputText value="#{msg['saisie11.prompt']}"/>
        <h:inputText id="saisie11" value="#{form.saisie11}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:panelGroup>
          <h:message for="saisie11" styleClass="error"/>
          <h:outputText value="#{form.errorSaisie11}" styleClass="error"/>
        </h:panelGroup>
        <h:outputText value="#{form.saisie11}"/>
        <!-- خط ۱۳ -->
        ...
          <h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
        ...

خطوط ۴–۷: کامپوننت saisie11 دارای دو پیام خطای ممکن است:

  • آن پیامی که نشان‌دهنده تبدیل نادرست یا داده‌های ناقص است. این پیام که توسط خود JSF تولید می‌شود، در یک نوع FacesMessage قرار گرفته و توسط تگ <h:message> در خط 5 نمایش داده می‌شود،
  • پیامی که در صورت عدم مساوی بودن entry11 + entry12 با 10 تولید خواهیم کرد. این پیام در خط 6 نمایش داده خواهد شد. پیام خطا در قالب form.errorSaisie11 قرار خواهد گرفت.

این دو پیام مربوط به خطاهایی هستند که نمی‌توانند همزمان رخ دهند. بررسی شرط «entry11 + entry12 = 10» در متد submit انجام می‌شود که تنها در صورتی اجرا می‌شود که خطای دیگری در فرم باقی نمانده باشد. تا زمانی که این متد اجرا شود، کامپوننت saisie11 بررسی شده و قالب آن، form.saise11، مقداردهی شده است. پیام در خط ۵ دیگر نمایش داده نخواهد شد. برعکس، اگر پیام در خط ۵ نمایش داده شود، این بدان معناست که حداقل یک خطا در فرم باقی مانده است و متد submit اجرا نخواهد شد. پیام در خط ۶ نمایش داده نخواهد شد. برای اطمینان از اینکه دو پیام خطای ممکن در ستون یکسان جدول ظاهر شوند، آن‌ها در تگ <h:panelGroup> (خطوط ۴ و ۷) گروه‌بندی شده‌اند.

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


  // اقدامات
  public String submit() {
    // آخرین اعتبارسنجی‌ها
    validateForm();
    // همان فرم بازگردانده می‌شود
    return null;
  }

  // اعتبارسنجی‌های کلی
  private void validateForm() {
    if ((saisie11 + saisie12) != 10) {
      // پیام کلی
      FacesMessage message = Messages.getMessage(null, "saisies11et12.incorrectes", null);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      FacesContext context = FacesContext.getCurrentInstance();
      context.addMessage(null, message);
      // پیام‌های مرتبط با فیلد
      message = Messages.getMessage(null, "error.sign", null);
      setErrorSaisie11(message.getSummary());
      setErrorSaisie12(message.getSummary());
    } else {
      setErrorSaisie11("");
      setErrorSaisie12("");
    }
}
  • خط ۴: متد submit برای انجام اعتبارسنجی‌های نهایی، متد validateForm را فراخوانی می‌کند،
  • خط ۱۱: بررسی می‌کند که آیا saisie11+saisie12=10 است،
  • اگر اینطور نباشد، در خطوط ۱۳–۱۴، پیامی از نوع FacesMessage با شناسه پیام saisies11et12.incorrectes ایجاد می‌شود. این به شرح زیر است:

saisies11et12.incorrectes=La propriété saisie11+saisie12=10 n'est pas vérifiée
  • پیام ایجادشده بدین ترتیب (در خطوط ۱۵–۱۶) به فهرست پیام‌های خطای برنامه اضافه می‌شود. این پیام به هیچ مؤلفه خاصی متصل نیست. این یک پیام سراسری برنامه است. این پیام توسط تگ <h:messages globalOnly="true"/> که در بالا نشان داده شده است، نمایش داده خواهد شد،
  • خط ۱۸: یک پیام جدید از نوع FacesMessage با شناسه پیام error.sign ایجاد می‌شود. این به شرح زیر است:

error.sign="!"

ما بیان کرده‌ایم که متد استاتیک [Messages.getMessage] یک پیام از نوع FacesMessage را با یک نسخه خلاصه و یک نسخه تفصیلی، در صورت وجود، ایجاد می‌کند. در اینجا، تنها نسخه خلاصه پیام error.sign وجود دارد. نسخهٔ خلاصهٔ پیام m از طریق m.getSummary() به دست می‌آید. خطوط ۱۹ و ۲۰: نسخه خلاصه پیام error.sign در فیلدهای errorSaisie11 و errorSaisie12 قالب قرار می‌گیرد. این موارد توسط تگ‌های زیر JSF نمایش داده می‌شوند:


          <h:outputText value="#{form.saisie11}"/>
          ...
          <h:outputText value="#{form.saisie12}"/>
  • رده‌های ۲۲–۲۳: اگر ویژگی saisie11+saisie12=10 علامت‌گذاری شود، دو فیلد errorSaisie11 و errorSaisie12 در قالب پاک می‌شوند تا هرگونه پیام خطای قبلی حذف شود. توجه به این نکته مهم است که قالب بین درخواست‌ها، در طول جلسهٔ مشتری، حفظ می‌شود.

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

توجه کنید که در ستون [1]، قالب مقادیر ارسال‌شده را دریافت کرده است، که نشان می‌دهد تمام عملیات اعتبارسنجی و تبدیل بین مقادیر ارسال‌شده و قالب با موفقیت انجام شده است. رویدادپرداز form.submit، که کلیک روی دکمه [Valider] را مدیریت می‌کند، بنابراین قادر به اجرا بود. این دست‌پردازنده رویداد بود که پیام‌های نمایش‌داده‌شده در [2] و [3] را تولید کرد. می‌بینیم که مدل به‌روزرسانی شد، هرچند فرم رد شده و به کلاینت بازگردانده شد. ممکن است در چنین حالتی خواسته شود که قالب به‌روزرسانی نشود. در واقع، اگر کاربر با استفاده از دکمه [Annuler] یا [4] به‌روزرسانی را لغو کند، بازگشت به قالب اصلی امکان‌پذیر نخواهد بود، مگر اینکه ذخیره شده باشد.

2.8.5.9. POST برای فرمی بدون اعتبارسنجی ورودی

بیایید فرم بالا را در نظر بگیریم و فرض کنیم کاربر، بدون آنکه متوجه اشتباهات خود باشد، می‌خواهد ارسال فرم را متوقف کند. در این صورت، او از دکمه [Annuler] استفاده خواهد کرد که توسط کد JSF زیر تولید شده است:


<!-- دکمه‌های فرمان -->
      <h:panelGrid columns="2">
        <h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
        <h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/>
      </h:panelGrid>

در خط ۴، پیام msg['cancel'] به شرح زیر است:


cancel=Annuler

متد form.cancel مرتبط با دکمه [Annuler] تنها در صورتی اجرا می‌شود که فرم معتبر باشد. این همان چیزی است که برای متد form.submit مرتبط با دکمه [Valider] نشان دادیم. اگر کاربر بخواهد ارسال فرم را لغو کند، طبیعتاً هیچ دلیلی برای بررسی اعتبار ورودی‌های او وجود ندارد. این نتیجه با استفاده از ویژگی immediate="true" حاصل می‌شود که به JSF دستور می‌دهد روش form.cancel را بدون طی کردن مرحله اعتبارسنجی و تبدیل اجرا کند. بیایید به چرخه پردازش برای POST و JSF بازگردیم:

رویدادها برای اجزای عملیاتی <h:commandButton> و <h:commandLink> با ویژگی immediate=&quot;true&quot; در فاز [C] پردازش می‌شوند، و پس از آن چرخه JSF مستقیماً به فاز [E] برای رندر کردن پاسخ ادامه می‌دهد.

روش form.cancel به شرح زیر است:


  public String cancel() {
    saisie1 = 0;
    saisie2 = 0;
    saisie3 = 0;
    saisie4 = 0;
    saisie5 = 0.0;
    saisie6 = 0.0;
    saisie7 = true;
    saisie8 = new Date();
    saisie9 = "";
    saisie10 = 0;
    return null;
}

اگر در فرم قبلی روی دکمه [Annuler] کلیک کنید، صفحه زیر بازگردانده می‌شود:

  • فرم دوباره نمایش داده می‌شود زیرا رویدادپرداز form.cancel کلید ناوبری null را بازمی‌گرداند. بنابراین صفحه [index.xhtml] بازگردانده می‌شود،
  • قالب [Form.java] توسط متد form.cancel تغییر داده شده است. این موضوع در ستون [2] که این قالب را نمایش می‌دهد، منعکس شده است،
  • در حالی که ستون [3] مقدار ارسال‌شده برای کامپوننت‌ها را منعکس می‌کند.

بیایید به کد JSF برای مؤلفه saisie1 [4] بازگردیم؛


          <!-- خط ۱ -->
          <h:outputText value="#{msg['saisie1.prompt']}"/>
          <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
          <h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>

در خط ۴، مقدار کامپوننت saisie1 به قالب form.saisie1 متصل است. این موضوع چندین پیامد دارد:

  • هنگامی که یک GET بر روی [index.xhtml] انجام می‌شود، کامپوننت saisie1 مقدار قالب form.saisie1 را نمایش خواهد داد،
  • وقتی یک POST از [index.xhtml] ایجاد می‌شود، مقدار ارسال‌شده برای کامپوننت saisie1 تنها در صورتی به قالب form.saisie1 اختصاص داده می‌شود که تمام اعتبارسنجی‌ها و تبدیلات فرم با موفقیت انجام شوند. صرف‌نظر از اینکه قالب با مقادیر ارسال‌شده به‌روزرسانی شده باشد یا خیر، اگر فرم پس از اجرای POST بازگردانده شود، کامپوننت‌ها مقداری را که ارسال شده نمایش می‌دهند و نه مقدار مربوط به قالب خود. این موضوع در تصویر اسکرین‌شات بالا نشان داده شده است، جایی که ستون‌های [2] و [3] مقادیر یکسانی ندارند.

2.9. مثال mv-jsf2-07: رویدادهای مربوط به تغییر وضعیت اجزای JSF

2.9.1. برنامه

برنامه مثالی از POST را بدون استفاده از دکمه یا لینک نشان می‌دهد. فرم به شرح زیر است:

محتوای لیست combo2 [2] به آیتم انتخاب‌شده در combo1 [1] پیوند داده شده است. هنگامی که انتخاب در [1] تغییر می‌کند، یک فرم POST ایجاد می‌شود، که در طول آن محتوای combo2 برای نمایش عنصر انتخاب‌شده در [1] تغییر داده شده و سپس فرم بازگردانده می‌شود. در طول این POST هیچ اعتبارسنجی انجام نمی‌شود.

2.9.2. پروژه NetBeans

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

یک فرم واحد به نام [index.xhtml] وجود دارد که قالب آن [Form.java] است.

2.9.3. محیط برنامه

فایل پیام [messages_fr.properties]:


app.titre=intro-07
app.titre2=JSF - Listeners
combo1.prompt=combo1
combo2.prompt=combo2
saisie1.prompt=Nombre entier de type int
submit=Valider
raz=Raz
data.required=Donnée requise
integer.required=Entrez un nombre entier
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du modèle du formulaire

فایل سبک [styles.css]:


.info{
   font-family: Arial,Helvetica,sans-serif;
   font-size: 14px;
   font-weight: bold
}

.col1{
   background-color: #ccccff
}

.col2{
   background-color: #ffcccc
}

.col3{
   background-color: #ffcc66
}

.col4{
   background-color: #ccffcc
}

.error{
   color: #ff0000
}

.saisie{
   background-color: #ffcccc;
   border-color: #000000;
   border-width: 5px;
   color: #cc0033;
   font-family: cursive;
   font-size: 16px
}

.combo{
  color: green;
}

.entete{
   font-family: 'Times New Roman',Times,serif;
   font-size: 14px;
   font-weight: bold
}

2.9.4. فرم [index.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:f="http://java.sun.com/jsf/core">
  <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');">
    <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
    <h:form id="formulaire">
      <h:messages globalOnly="true"/>
      <h:panelGrid columns="4" border="1" 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['saisie.erreur']}" styleClass="entete"/>
        <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
        <!-- خط ۱ -->
        <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}"/>
        <!-- خط ۲ -->
        <h:outputText value="#{msg['combo2.prompt']}"/>
        <h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
          <f:selectItems value="#{form.combo2Items}"/>
        </h:selectOneMenu>
        <h:panelGroup></h:panelGroup>
        <h:outputText value="#{form.combo2}"/>
        <!-- خط ۳ -->
        <h:outputText value="#{msg['saisie1.prompt']}"/>
        <h:inputText id="saisie1" value="#{form.saisie1}" required="true" requiredMessage="#{msg['data.required']}" styleClass="saisie" converterMessage="#{msg['integer.required']}"/>
        <h:message for="saisie1" styleClass="error"/>
        <h:outputText value="#{form.saisie1}"/>
      </h:panelGrid>
      <!-- دکمه‌های کنترلی -->
      <h:panelGrid columns="2" border="0">
        <h:commandButton value="#{msg['submit']}"/>
        ...
      </h:panelGrid>
    </h:form>
  </h:body>
</html>

تغییر در کد لیست combo1، خطوط ۲۴–۲۶ است. ویژگی‌های جدیدی اضافه شده‌اند:

  • onchange: ویژگی HTML – یک تابع یا کد جاوااسکریپت را اعلام می‌کند که باید هنگام تغییر عنصر انتخاب‌شده در combo1 اجرا شود. در اینجا، کد جاوااسکریپت submit() فرم را به سرور ارسال می‌کند،
  • valueChangeListener: ویژگیِ JSF – نام متدی را مشخص می‌کند که باید در سرور اجرا شود، زمانی که عنصر انتخاب‌شده در combo1 تغییر می‌کند. در مجموع، دو متد اجرا می‌شوند: یکی در سمت کلاینت و دیگری در سمت سرور،
  • immediate=true: ویژگی JSF – مشخص می‌کند که دست‌پردازنده رویداد سمت سرور چه زمانی اجرا شود: پس از پر شدن مجدد فرم با ورودی‌های کاربر اما قبل از بررسی‌های اعتبارسنجی ورودی. هدف در اینجا پر کردن لیست combo2 بر اساس مورد انتخاب‌شده در لیست combo1 است، حتی اگر ممکن است ورودی‌های نادرستی در سایر بخش‌های فرم وجود داشته باشد. در اینجا یک مثال آورده شده است:
  • در [1]، یک ورودی اولیه،
  • در [2]، عنصر انتخاب‌شده از combo1 از A به B تغییر داده می‌شود.

نتیجه به شرح زیر است:

POST انجام شده است. محتوای combo2 و [2] باعنصر منتخب در combo1 و [1]، با وجود اینکه ورودی در [3] نادرست بود، انتخاب شد. این ویژگی immediate=true بود که باعث شد متد form.combo1ChangeListener قبل از بررسی‌های اعتبار اجرا شود. بدون این ویژگی، این متد اجرا نمی‌شد، زیرا چرخه پردازش به دلیل خطا در [3] در بررسی‌های اعتبارسنجی متوقف می‌شد.

پیام‌های مرتبط با فرم در [messages.properties] به شرح زیر است:


app.titre=intro-07
app.titre2=JSF - Listeners
combo1.prompt=combo1
combo2.prompt=combo2
saisie1.prompt=Nombre entier de type int
submit=Valider
raz=Raz
data.required=Donnée requise
integer.required=Entrez un nombre entier
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du modèle du formulaire

دوره عمر [Form.java] روی 'درخواست' تنظیم شده است:


package forms;

...

@ManagedBean
@RequestScoped
public class Form {

در خط ۶، دامنهٔ bean روی 'request' تنظیم شده است.

2.9.5. قالب [Form.java]

قالب [Form.java] به شرح زیر است:


package forms;

import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

@ManagedBean
@RequestScoped
public class Form {
  
  public Form() {
  }
  
// fields
  private String combo1="A";
  private String combo2="A1";
  private Integer saisie1=0;
  
  // fields of work
  final private String[] combo1Labels={"A","B","C"};
  private String combo1Label="A";
  private static final Logger logger=Logger.getLogger("forms.Form");
  
  // روش‌ها
  public SelectItem[] getCombo1Items(){
    // ابتدای combo1
    SelectItem[] combo1Items=new SelectItem[combo1Labels.length];
    for(int i=0;i<combo1Labels.length;i++){
      combo1Items[i]=new SelectItem(combo1Labels[i],combo1Labels[i]);
    }
    return combo1Items;
  }
  
  public SelectItem[] getCombo2Items(){
    //init combo2 مبتنی بر combo1
    SelectItem[] combo2Items=new SelectItem[5];
    for(int i=1;i<=combo2Items.length;i++){
      combo2Items[i-1]=new SelectItem(combo1Label+i,combo1Label+i);
    }
    return combo2Items;
  }
  
  // شنوندگان
  public void combo1ChangeListener(ValueChangeEvent event){
    // ردیابی
    logger.info("combo1ChangeListener");
    // ارزش ارسال‌شده از combo1 را بازیابی می‌کند
    combo1Label=(String)event.getNewValue();
    //پاسخ را بازمی‌گرداند زیرا می‌خواهیم اعتبارسنجی‌ها را دور بزنیم
    FacesContext.getCurrentInstance().renderResponse();
  }
  
  public String raz(){
    //پیگیری
    logger.info("raz");
    // پاک کردن فرم
    combo1Label="A";
    combo1="A";
    combo2="A1";
    saisie1=0;
    return null;
  }
  
// گیرنده و تنظیم‌کننده
  ...
}

بیایید فرم [index.xhtml] را به قالب آن [Form.java] پیوند دهیم:

لیست combo1 توسط کد زیر JSF تولید می‌شود:


        <h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
</h:selectOneMenu>

این عناصر خود را از طریق متد getCombo1Items در قالب خود (خط ۲) دریافت می‌کند. این متد در خطوط ۲۸–۳۵ کد جاوا تعریف شده است. این متد یک لیست سه‌عنصری {"A", "B", "C"} تولید می‌کند.

لیست combo2 توسط کد زیر JSF تولید می‌شود:


        <h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
          <f:selectItems value="#{form.combo2Items}"/>
</h:selectOneMenu>

این متد عناصر خود را از طریق متد getCombo2Items مدل خود (خط ۲) دریافت می‌کند. این متد در خطوط ۳۷–۴۴ کد جاوا تعریف شده است. این متد یک لیست پنج عنصری ایجاد می‌کند {"X1", "X2", "X3", "X4", "X5"}، که در آن X عنصر combo1Label از خط 16 است. بنابراین، هنگامی که فرم برای اولین بار ایجاد می‌شود، لیست combo2 شامل عناصر {"A1","A2","A3", "A4", "A5"}.

هنگامی که کاربر آیتم انتخاب‌شده در لیست combo1 را تغییر می‌دهد،

  • رویداد onchange="submit();" توسط مرورگر سمت کلاینت پردازش خواهد شد. بنابراین فرم به سرور ارسال می‌شود؛
  • در سمت سرور، JSF تشخیص می‌دهد که مقدار کامپوننت combo1 تغییر کرده است. متد combo1ChangeListener در خطوط ۴۷–۵۴ اجرا خواهد شد. یک متد از نوع ValueChangeListener یک شیء از نوع javax.faces.event.ValueChangeEvent را به‌عنوان پارامتر دریافت می‌کند. این شیء امکان بازیابی مقادیر قدیمی و جدید کامپوننتی را که تغییر کرده است، با استفاده از روش‌های زیر فراهم می‌کند:

Image

در اینجا، کامپوننت، لیست combo1 از نوع UISelectOne است. مقدار آن از نوع String است.

  • خط ۵۱ از قالب جاوا: مقدار جدید combo1 در combo1Label ذخیره می‌شود، که برای تولید عناصر لیست combo2 استفاده می‌شود،
  • خط ۵۳: پاسخ بازگردانده می‌شود. مهم است که در اینجا توجه شود مدیر combo1ChangeListener با ویژگی immediate="true" اجرا می‌شود. بنابراین پس از مرحله‌ای که درخت مؤلفه صفحه با مقادیر ارسال‌شده به‌روزرسانی شده و قبل از فرآیند اعتبارسنجی مقادیر ارسال‌شده اجرا می‌شود. با این حال، ما می‌خواهیم این فرآیند اعتبارسنجی را دور بزنیم زیرا لیست combo2 باید حتی اگر هنوز ورودی‌های نادرستی در فرم وجود داشته باشد، به‌روزرسانی شود. بنابراین، ما درخواست می‌کنیم که پاسخ فوراً ارسال شود بدون اینکه از مرحله اعتبارسنجی داده‌ها عبور کند.
  • فرم دقیقاً همان‌طور که وارد شده است ارسال خواهد شد. با این حال، مقادیر وارد شده در لیست‌های combo1 و combo2، مقادیر ارسال‌شده (posted values) نیستند. آنها با فراخوانی متدهای getCombo1Items و getCombo2Items مجدداً تولید خواهند شد. این روش دوم سپس از مقدار جدید combo1Label که توسط combo1ChangeListener تنظیم شده است استفاده می‌کند و عناصر لیست combo2 تغییر خواهند کرد.

2.9.6. دکمه [Raz]

ما می‌خواهیم از دکمه [Raz] برای بازنشانی فرم به وضعیت اولیه آن استفاده کنیم، همان‌طور که در زیر نشان داده شده است:

در [1]، فرم به شکلی که قبل از دکمه POST ([Raz]) بود؛ در [2]، نتیجه POST.

اگرچه از نظر عملکردی ساده است، اما رسیدگی به این مورد استفاده کاملاً پیچیده است. راه‌حل‌های مختلفی را می‌توان امتحان کرد، از جمله راه‌حلی که در مثال قبلی برای دکمه [Annuler] استفاده شد:


       <h:commandButton value="#{msg['raz']}" immediate="true" action="#{form.raz}"/>

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


  public String raz(){
    // پاک کردن فرم
    combo1Label="A";
    combo1="A";
    combo2="A1";
    saisie1=0;
    return null;
}

بنابراین نتیجه تولید شده توسط دکمه [Raz] در مثال قبلی به شرح زیر است:

ستون [1] نشان می‌دهد که متد form.raz اجرا شده است. با این حال، ستون [1] همچنان مقادیر ارسال‌شده را نمایش می‌دهد:

  • برای combo1، مقدار ارسال‌شده «B» بود. بنابراین این مورد از لیست انتخاب می‌شود؛
  • برای combo2، مقدار ارسال‌شده «B5» بود. در نتیجه اجرای form.raz، ورودی‌های {"B1"، ..., "B5"} از combo2 به {"A1", ..., "A5"} تغییر یافتند. عنصر "B5" دیگر وجود ندارد و بنابراین قابل انتخاب نیست. سپس اولین عنصر در لیست نمایش داده می‌شود؛
  • برای saisie1، مقدار ارسال‌شده ۱۰ بود.

این رفتار عادی زمانی است که ویژگی immediate=&quot;true&quot; تنظیم شده باشد. برای دستیابی به نتیجه متفاوت، باید مقادیری را که می‌خواهید در فرم جدید ببینید ارسال کنید، حتی اگر کاربر مقادیر متفاوتی وارد کرده باشد. این کار با کمی جاوااسکریپت سمت کلاینت انجام می‌شود. فرم سپس به شکل زیر درمی‌آید:


<script language="javascript">
  function raz(){
    document.forms['formulaire'].elements['formulaire:combo1'].value="A";
    document.forms['formulaire'].elements['formulaire:combo2'].value="A1";
    document.forms['formulaire'].elements['formulaire:saisie1'].value=0;
    //document.forms['formulaire'].submit();
  }
</script>
...
<h:commandButton value="#{msg['raz']}" onclick='raz()' immediate="true" action="#{form.raz}"/>
  • خط ۱۰: ویژگی onclick='raz()' به مرورگر دستور می‌دهد تا تابع جاوااسکریپت raz را هنگامی که کاربر روی دکمه [Raz] کلیک می‌کند، اجرا کند،
  • خط ۳: مقدار «A» به عنصر HTML با نام «form:combo1» اختصاص داده می‌شود. عناصر مختلف در خط ۳ به شرح زیر هستند:
    • document: صفحه‌ای که توسط مرورگر نمایش داده می‌شود،
    • document.forms: تمام فرم‌های موجود در سند،
    • document.forms['formulaire']: فرم با ویژگی name="formulaire",
    • documents.forms['formulaire'].elements: مجموعه عناصر فرم با ویژگی name="formulaire",
    • document.forms['formulaire'].elements['formulaire:combo1']: عنصر فرم با ویژگی name="formulaire:combo1"
    • document.forms['formulaire'].elements['formulaire:combo1'].value: مقداری که باید توسط عنصر فرم با ویژگی name="formulaire:combo1". ارسال شود

برای یافتن ویژگی‌های name عناصر مختلف در صفحه‌ای که توسط مرورگر نمایش داده می‌شود، می‌توانید کد منبع آن را مشاهده کنید (در زیر با IE7 نشان داده شده است):

<form id="formulaire" name="formulaire" ...>
...
<select id="formulaire:combo1" name="formulaire:combo1" ...>

با توضیح این موضوع، واضح است که در کد جاوااسکریپت برای تابع raz:

  • خط ۳ تضمین می‌کند که مقدار ارسال‌شده برای کامپوننت combo1، رشته A خواهد بود،
  • خط ۴ تضمین می‌کند که مقدار ارسال‌شده به کامپوننت combo2، رشته A1 خواهد بود،
  • خط ۵ تضمین می‌کند که مقدار ارسال‌شده برای کامپوننت saisie1، رشته 0 خواهد بود.

پس از انجام این کار، دکمه POST در فرم، که با هر دکمه از نوع <h:commandButton> (خط ۱۰) مرتبط است، فراخوانی خواهد شد. متد form.raz اجرا شده و فرم همان‌طور که ارسال شده بود، بازگردانده می‌شود. این کار نتیجه زیر را تولید می‌کند:

این نتیجه موارد زیادی را پنهان می‌کند. مقادیر «A»، «A1» و «0» برای کامپوننت‌های combo1، combo2 و saisie1 به سرور ارسال می‌شوند. فرض کنید مقدار قبلی combo1 برابر با «B» بوده است. در این صورت، تغییری در مقدار کامپوننت combo1 رخ می‌دهد و متد form.combo1ChangeListener نیز باید اجرا شود. دو رویدادپرداز با ویژگی immediate="true" وجود دارند. آیا هر دو اجرا خواهند شد؟ اگر چنین است، به چه ترتیبی؟ فقط یکی؟ اگر چنین است، کدام یک؟

برای کسب اطلاعات بیشتر، ما در برنامه در حال ایجاد لاگ هستیم:


package forms;

import java.util.logging.Logger;
...
public class Form {
  
...  
// میدان‌های فرم
  private String combo1="A";
  private String combo2="A1";
  private Integer saisie1=0;
  
  // fields of work
  final private String[] combo1Labels={"A","B","C"};
  private String combo1Label="A";
  private static final Logger logger=Logger.getLogger("forms.Form");
  
  //شنونده
  public void combo1ChangeListener(ValueChangeEvent event){
    // ردیابی
    logger.info("combo1ChangeListener");
    // ارزش ارسال‌شده را از combo1 بازیابی کنید
    combo1Label=(String)event.getNewValue();
    //پاسخ را بازمی‌گرداند زیرا می‌خواهیم اعتبارسنجی‌ها را دور بزنیم
    FacesContext.getCurrentInstance().renderResponse();
  }
  
  public String raz(){
    //پیگیری
    logger.info("raz");
    // پاک کردن فرم
    combo1Label="A";
    combo1="A";
    combo2="A1";
    saisie1=0;
    return null;
  }
...
}
  • خط ۱۶: یک تولیدکننده لاگ ایجاد می‌شود. پارامتر برای getLogger به ما امکان می‌دهد تا بین منابع لاگ‌ها تمایز قائل شویم. در اینجا، لاگ‌گیر forms.Form نامیده می‌شود،
  • خط ۲۱: ما فراخوانی متد combo1ChangeListener را ثبت می‌کنیم،
  • خط ۳۰: یک ورودی برای فراخوانی متد raz ثبت می‌شود.

چه لاگ‌هایی توسط دکمه [Raz] یا با تغییر مقدار combo1 تولید می‌شوند؟ بیایید سناریوهای مختلف را بررسی کنیم:

  • دکمه [Raz] زمانی استفاده می‌شود که عنصر انتخاب‌شده در combo1 «A» باشد. بنابراین «A» آخرین مقدار مؤلفه combo1 است. ما دیدیم که دکمه [Raz] یک تابع جاوااسکریپت را اجرا می‌کند که مقدار «A» را برای کامپوننت combo1 ارسال می‌کند. بنابراین مقدار کامپوننت combo1 تغییر نمی‌کند. سپس لاگ‌ها نشان می‌دهند که تنها متد form.raz اجرا می‌شود:
  
  • دکمه [Raz] استفاده می‌شود، در حالی که عنصر انتخاب‌شده در combo1 «A» نیست. بنابراین کامپوننت combo1 مقدار خود را تغییر می‌دهد: مقدار قبلی آن «A» نبود و دکمه [Raz] مقدار آن را روی «A» تنظیم خواهد کرد. سپس لاگ‌ها نشان می‌دهند که دو متد اجرا شده‌اند. به ترتیب: combo1ChangeListener، raz:
  
  • ما مقدار combo1 را بدون استفاده از دکمه [Raz] تغییر می‌دهیم. لاگ‌ها نشان می‌دهند که تنها متد combo1ChangeListener اجرا می‌شود:
  

2.10. مثال mv-jsf2-08: تگ <h:dataTable>

2.10.1. برنامه

برنامه فهرستی از افراد را همراه با گزینه حذف آن‌ها نمایش می‌دهد:

  • در [1]، فهرستی از افراد،
  • در [2]، لینک‌هایی که امکان حذف آن‌ها را فراهم می‌کنند.

2.10.2. پروژه NetBeans

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

یک فرم واحد به نام [index.xhtml] وجود دارد که قالب آن [Form.java] است.

2.10.3. محیط برنامه

فایل پیکربندی [faces-config.xml]:


<?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>
  </application>
</faces-config>

فایل پیام [messages_fr.properties]:


app.titre=intro-08
app.titre2=JSF - DataTable
submit=Valider
personnes.headers.id=Id
personnes.headers.nom=Nom
personnes.headers.prenom=Pr\u00e9nom

فایل سبک [styles.css]:


.headers {
   text-align: center;
   font-style: italic;
   color: Snow;
   background: Teal;
}

.id {
   height: 25px;
   text-align: center;
   background: MediumTurquoise;
}

.nom {
   text-align: left;
   background: PowderBlue;
}
.prenom {
   width: 6em;
   text-align: left;
   color: Black;
   background: MediumTurquoise;
}

2.10.4. فرم [index.xhtml] و قالب آن [Form.java]

بیایید نمای مرتبط با صفحه [index.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:f="http://java.sun.com/jsf/core">
  <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');">
    <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
    <h:form id="formulaire">
      <h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
  ........................
      </h:dataTable>
    </h:form>
  </h:body>
</html>

در خط ۱۴، تگ <h:dataTable> از فیلد #{form.personnes} به‌عنوان منبع داده‌های خود استفاده می‌کند. این فیلد به شرح زیر است:

private List<Person> people;

کلاس [Personne] به صورت زیر است:


package forms;

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

بیایید به محتوای تگ <h:dataTable> بازگردیم:


<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
...
</h:dataTable>
  • ویژگی var="person" نام متغیری را که نماینده شخص فعلی در تگ <h:datatable> است، تعیین می‌کند،
  • ویژگی headerClass="headers" سبک سربرگ‌های ستون جدول را تعیین می‌کند،
  • ویژگی columnClasses="...." سبک هر ستون در جدول را تعیین می‌کند.

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

  

کد XHTML برای ستون Id به شرح زیر است:


<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['personnes.headers.id']}"/>
          </f:facet>
          <h:outputText value="#{personne.id}"/>
        </h:column>
        ...
      </h:dataTable>

lignes 3-5 : la balise <f:facet name="header"> définit le titre de la colonne,
ligne 4 : le titre de la colonne est pris dans le fichier des messages,
ligne 6 : personne fait référence à l'attribut var de la balise <h:dataTable ...> (ligne 1). On écrit donc l'id de la personne courante.


<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['personnes.headers.id']}"/>
          </f:facet>
          <h:outputText value="#{personne.id}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['personnes.headers.nom']}"/>
          </f:facet>
          <h:outputText value="#{personne.nom}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['personnes.headers.prenom']}"/>
          </f:facet>
          <h:outputText value="#{personne.prénom}"/>
        </h:column>
...
      </h:dataTable>
  • رده‌های ۳–۷: ستون «id» جدول،
  • رده‌های ۸–۱۳: ستون «نام خانوادگی» جدول،
  • رده‌های 14–19: ستون «نام» جدول.

اکنون، بیایید به ستون لینک‌ها [Retirer] نگاه کنیم:

این ستون با کد زیر تولید می‌شود:


<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
...
        <h:column>
          <h:commandLink value="Retirer" action="#{form.retirerPersonne}">
            <f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
          </h:commandLink>
        </h:column>
      </h:dataTable>

لینک [Retirer] توسط خطوط 4–6 تولید می‌شود. وقتی روی لینک کلیک می‌شود، متد [Form].retirerPersonne اجرا خواهد شد. اکنون زمان بررسی کلاس [Form.java] است:


package forms;

import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class Form {

  // الگو
  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 String retirerPersonne() {
    // جستجو برای شخص انتخاب‌شده
    int i = 0;
    for (Personne personne : personnes) {
      // فرد فعلی = فرد انتخاب‌شده؟
      if (personne.getId() == personneId) {
        // حذف شخص فعلی از فهرست
        personnes.remove(i);
        // فرآیند کامل شد
        break;
      } else {
        // فرد بعدی
        i++;
      }
    }
    // آزمایش روی همان صفحه
    return null;
  }
  
  // گیرنده‌ها و تنظیم‌کننده‌ها
...
}
  • خطوط ۱۸–۲۴: سازنده، لیست افراد را از خط ۱۴ مقداردهی اولیه می‌کند،
  • خط ۱۰: از آنجا که این لیست باید در طول درخواست‌ها پایدار بماند، دامنه bean جلسه است.

وقتی متد [retirerPersonne] در خط 26 اجرا می‌شود، فیلد در خط 15 با شناسه فردی که لینک [Retirer] او کلیک شده بود، مقداردهی اولیه شده است:


          <h:commandLink value="Retirer" action="#{form.retirerPersonne}">
            <f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
</h:commandLink>

تگ <f:setPropertyActionListener> برای انتقال اطلاعات به قالب استفاده می‌شود. در اینجا، مقدار ویژگی 'value' به فیلد قالب که توسط ویژگی 'target' مشخص شده است، کپی می‌شود. بدین ترتیب، شناسه شخص فعلی – کسی که قرار است از لیست افراد حذف شود – از طریق گتر (getter) آن فیلد، به فیلد [Form].personneId کپی می‌شود. این کار قبل از اجرای متدی که در ویژگی action در خط 1 به آن ارجاع داده شده است، انجام می‌شود.

خطوط 26–43: متد [supprimerPersonne] فردی را که id آن برابر personneId است، حذف می‌کند.

2.11. Example mv-jsf2-09: چیدمان یک برنامه JSF

2.11.1. برنامه

این برنامه نشان می‌دهد چگونه یک برنامه JSF را با دو نما پیاده‌سازی کرد:

این برنامه دو نما دارد:

  • در [1]، صفحه ۱،
  • در [2]، صفحهٔ ۲.

می‌توانید بین این دو صفحه جابجا شوید. آنچه می‌خواهیم در اینجا نشان دهیم این است که صفحات ۱ و ۲ طرح‌بندی مشترکی دارند، همان‌طور که در اسکرین‌شات‌های بالا نشان داده شده است.

2.11.2. پروژه NetBeans

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

برنامه تنها شامل صفحاتی با شناسه XHTML است. هیچ قالب جاوا مرتبطی وجود ندارد.

2.11.3. صفحه [layout.xhtml]

صفحه [layout.xhtml] طرح‌بندی صفحات برنامه را تعریف می‌کند:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <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: 400px">
        <tr>
          <td colspan="2" bgcolor="#ccccff">
            <ui:include src="entete.xhtml"/>
          </td>
        </tr>
        <tr style="height: 200px">
          <td bgcolor="#ffcccc">
            <ui:include src="menu.xhtml"/>
          </td>
          <td>
            <ui:insert name="contenu" >
              <h2>Contenu</h2>
            </ui:insert>
          </td>
        </tr>
        <tr bgcolor="#ffcc66">
          <td colspan="2">
            <ui:include src="basdepage.xhtml"/>
          </td>
        </tr>         
      </table>
    </h:form>
  </h:body>
</html>

در خط ۷، یک فضای نام جدید به نام ui ظاهر می‌شود. این فضای نام حاوی تگ‌هایی است که برای قالب‌بندی صفحات یک برنامه استفاده می‌شوند. تگ‌های این فضای نام در خطوط ۱۷، ۲۲، ۲۵ و ۳۲ استفاده شده‌اند.

صفحه [layout.xhtml] اطلاعات را در جدولی به نام HTML (خط 14) نمایش می‌دهد. این صفحه را می‌توان از طریق مرورگر دسترسی پیدا کرد:

  • در [1]، URL درخواست‌شده.

ستون [2] توسط کد زیر XHTML تولید شده است:


  <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
    <h:form id="formulaire">
      <table style="width: 400px">
        <tr>
          <td colspan="2" bgcolor="#ccccff">
            <ui:include src="entete.xhtml"/>
          </td>
        </tr>
...       
      </table>
    </h:form>
</h:body>

تگ <ui:include> در خط ۶ اجازه می‌دهد کد خارجی XHTML در صفحه گنجانده شود. فایل [entete.xhtml] به شرح زیر است:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
  <body>
    <h2>entête</h2>
  </body>
</html>

تمام کد از خطوط ۳ تا ۸ در [layout.xhtml] درج خواهد شد. بنابراین، تگ‌های <html> و <body> در داخل یک تگ <td> درج خواهند شد. این کار باعث ایجاد هیچ خطایی نمی‌شود. بنابراین، صفحاتی که از طریق <ui:include> وارد می‌شوند، صفحات کامل XHTML هستند. از نظر بصری، تنها خط ۶ تأثیر خواهد داشت. تگ‌های <html> و <body> به دلایل نحوی وجود دارند.

بخش [3] توسط کد XHTML زیر تولید شده است:


<h:form id="formulaire">
      <table style="width: 400px">
        <tr style="height: 200px">
          <td bgcolor="#ffcccc">
            <ui:include src="menu.xhtml"/>
          </td>
...
        </tr>
...
      </table>
    </h:form>

تگ <ui:include> در خط ۵ فایل زیر را شامل می‌شود: [menu.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">
  <body>
    <h2>menu</h2>
  </body>
</html>

میدان [4] توسط کد زیر تولید شد: XHTML


<h:form id="formulaire">
      <table style="width: 400px">
...
        <tr bgcolor="#ffcc66">
          <td colspan="2">
            <ui:include src="basdepage.xhtml"/>
          </td>
        </tr>         
      </table>
    </h:form>

تگ <ui:include> در خط ۶ فایل زیر را شامل می‌شود: [basdepage.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">
  <body>
    <h2>bas de page</h2>
  </body>
</html>

میدان [5] توسط کد زیر تولید شد: XHTML


    <h:form id="formulaire">
...
          <td>
            <ui:insert name="contenu" >
              <h2>Contenu</h2>
            </ui:insert>
          </td>
 ...
      </table>
</h:form>

تگ <ui:insert> در خط ۵ یک فیلد به نام «content» را تعریف می‌کند. این فیلدی است که می‌تواند محتوای متغیر را در خود جای دهد. خواهیم دید چگونه. وقتی صفحه [layout.xhtml] را درخواست کردیم، هیچ محتوایی برای ناحیه‌ای به نام «content» تعریف نشده بود. در این حالت، محتوای تگ <ui:insert> در خطوط ۴–۶ استفاده می‌شود. بنابراین خط ۵ نمایش داده می‌شود.

2.11.4. صفحه [page1.xhtml]

صفحه [layout.xhtml] برای مشاهده در نظر گرفته نشده است. این صفحه به‌عنوان الگویی برای صفحات [page1.xhtml] و [page2.xhtml] عمل می‌کند. به این مورد، الگوی صفحه گفته می‌شود. صفحه [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: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>page 1</h2>
      <h:commandLink value="page 2" action="page2"/>
    </ui:define>
  </ui:composition>
</html>
  • در خط ۶، از فضای نام ui استفاده می‌شود؛
  • در خط ۷، مشخص شده است که صفحه با استفاده از تگ <ui:composition> به قالب [layout.xhtml] مرتبط می‌شود،
  • در خط ۸، این ارتباط به این معناست که هر تگ <ui:define> به یک تگ <ui:insert> در قالب در حال استفاده، در این مورد [layout.xhtml]، پیوند داده می‌شود. این پیوند از طریق ویژگی 'name' هر دو تگ برقرار می‌شود. این ویژگی‌ها باید یکسان باشند.

صفحه نمایش‌داده‌شده [layout.xhtml] است، جایی که محتوای هر تگ <ui:insert> با محتوای تگ <ui:define> از صفحه درخواست‌شده جایگزین می‌شود. در اینجا، گویی صفحه نمایش‌داده‌شده چنین است:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <h: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: 400px">
        <tr>
          <td colspan="2" bgcolor="#ccccff">
            <ui:include src="entete.xhtml"/>
          </td>
        </tr>
        <tr style="height: 200px">
          <td bgcolor="#ffcccc">
            <ui:include src="menu.xhtml"/>
          </td>
          <td>
              <h2>page 1</h2>
              <h:commandLink value="page 2" action="page2"/>
          </td>
        </tr>
        <tr bgcolor="#ffcc66">
          <td colspan="2">
            <ui:include src="basdepage.xhtml"/>
          </td>
        </tr>         
      </table>
    </h:form>
  </h:body>
</html>

خطوط ۲۵–۲۶ از [page1.xhtml] به جای تگ <ui:insert> از [layout.xml] درج شده‌اند.

صفحه [page2.xhtml] مشابه [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: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>page 2</h2>
      <h:commandLink value="page 1" action="page1"/>
    </ui:define>
  </ui:composition>
</html>

2.12. Conclusion

تحلیل انجام‌شده بر روی JSF 2 به هیچ وجه جامع نیست. با این حال، برای درک مثال‌های بعدی کافی است. برای جزئیات بیشتر، لطفاً به [ref2] مراجعه کنید.

2.13. آزمون با اکلیپس

بیایید ببینیم چگونه می‌توان پروژه‌های Maven را با استفاده از مجموعه ابزار SpringSource تست کرد:

  • در [1]، ما یک پروژه Maven [2] را وارد می‌کنیم که با استفاده از دکمه [3] انتخاب می‌کنیم. در اینجا، ما از پروژه Maven [mv-jsf2-09] برای Eclipse استفاده می‌کنیم
  • در [4]، پروژه واردشده به درستی به عنوان یک پروژه Maven [5] شناسایی شده است،
  • به [6]؛ پروژه وارد شده به کاوشگر پروژه (Project Explorer
  • در [7]، این پروژه روی سرور Tomcat اجرا می‌شود [8] [9]،
  • در [10]، Tomcat 7 راه‌اندازی شده است،
  • در [11]، صفحهٔ اصلی پروژهٔ [mv-jsf2-09] [11] در یک مرورگر درون Eclipse نمایش داده می‌شود.