Skip to content

9. المثال 07 – علامات النماذج

سنقوم بإنشاء واستخدام النموذج التالي:

Image

Image

سنوسع مفهوم إدخال المعلمات الذي رأيناه في حقل إدخال النص ليشمل عناصر HTML أخرى في النموذج.

9.1. مشروع NetBeans

  • في [1]، طرق عرض المشروع [Formx.jsp]
  • في [2]، وملف تكوين Struts والرسائل المُعَلَّمة
  • في [3]، والإجراءات [Formx.java]، وملف تكوين Struts آخر.

9.2. تكوين Struts

ملف [struts.xml] هو كما يلي:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
 
<struts>
  <constant name="struts.custom.i18n.resources" value="messages" />
 
  <include file="example/example.xml"/>
 
  <package name="default" namespace="/" extends="struts-default">
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Form</param>
        <param name="namespace">/example</param>
      </result>
    </action>
  </package>
</struts>

  • السطر 9: يتضمن ملف تكوين آخر [example.xml]. يقوم هذا الملف بتكوين الإجراءات.
  • الأسطر 11–19: الحزمة الافتراضية. تحدد عنوان إعادة التوجيه لعنوان URL /. سيتم إعادة توجيهه إلى عنوان URL /example/Form.action.

ملف [example.xml] الذي يقوم بتكوين الإجراءات هو كما يلي:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
 
<struts>
  <package name="example" namespace="/example" extends="struts-default">
    <action name="Form" class="example.Form">
      <result name="success">/example/Form.jsp</result>
    </action>
    <action name="Form1" class="example.Form1">
      <result name="success">/example/Form1.jsp</result>
    </action>
    <action name="Form2" class="example.Form2">
      <result name="success">/example/Form2.jsp</result>
    </action>
  </package>
</struts>
  • الأسطر 7–17: تحدد الإجراءات /example/Form و /example/Form1 و /example/Form2، حيث /example هو مساحة الاسم (السطر 7) و Formx هي الإجراءات.
  • الأسطر 8-10: سيؤدي تنفيذ الإجراء Form إلى عرض طريقة العرض /example/Form.jsp.
  • الأسطر 11-13: سيؤدي تنفيذ الإجراء Form1 إلى عرض طريقة العرض /example/Form1.jsp.
  • الأسطر 14–16: سيؤدي تنفيذ الإجراء Form2 إلى عرض طريقة العرض /example/Form2.jsp.

9.3. ملفات الرسائل

لن نتطرق إلى تدويل المشروع. فقد تناولنا هذا الموضوع بالفعل في المثال 01. نقدم محتويات ملف [messages.properties] حتى يتمكن القارئ من فهم العروض التالية.


Form.francais=Fran\u00E7ais
Form.anglais=Anglais
Form.titre=Struts 2 - les tags de formulaire
Form.message=Struts 2 - les tags de formulaire
Form.langues=langues
Form.textfield=1-textfield
Form.password=2-password
Form.textarea=3-textarea
Form.select1=4-select (multiple=false, size=1)
Form.select1.header=<-- select1 -->
Form.select2=5-select (multiple=false, size=3)
Form.select3=6-select (multiple=true, size=3)
Form.radio=7-radio
Form.checkbox=8-checkbox
Form.checkboxlist=9-checkboxlist
Form.hidden=10-hidden
Form.submitText=Valider
Form.buttonRazText=Raz
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.textfield=1-textfield
Confirmation.password=2-password
Confirmation.textarea=3-textarea
Confirmation.select1=4-select (multiple=false, size=1)
Confirmation.select2=5-select (multiple=false, size=3)
Confirmation.select3=6-select (multiple=true, size=3)
Confirmation.radio=7-radio
Confirmation.checkbox=8-checkbox
Confirmation.checkboxlist=9-checkboxlist
Confirmation.hidden=10-hidden

9.4. عرض [Form.jsp] – قسم الإدخال

يبدو كما يلي:

Image

وهو كما يلي:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Form.titre"/></title>
    <s:head/>
  </head>
 
  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="Form.message"/></h2>
    <h3><s:text name="Form.langues"/></h3>
    <ul>
      <li>
        <s:url id="url" action="Form">
          <s:param name="request_locale">en</s:param>
        </s:url>
        <s:a href="%{url}"><s:text name="Form.anglais"/></s:a>
      </li>
      <li>
        <s:url id="url" action="Form">
          <s:param name="request_locale">fr</s:param>
        </s:url>
        <s:a href="%{url}"><s:text name="Form.francais"/></s:a>
      </li>
    </ul>
    <s:form name="formulaire">
      <s:textfield name="textfield" key="Form.textfield" />
      <s:password name="password" key="Form.password"/>
      <s:textarea name="textarea" key="Form.textarea" cols="40" rows="5"/>
      <s:select name="select1" list="select1Values" size="1" key="Form.select1" headerValue="<-- select 1 -->" headerKey="-1" />
      <s:select name="select2" size="3" list="select2Values" key="Form.select2"/>
      <s:select name="select3" size="3" list="select3Values" key="Form.select3" multiple="true"/>
      <s:radio name="radio" list="radioValues" key="Form.radio"/>
      <s:checkbox name="checkbox" key="Form.checkbox"/>
      <s:checkboxlist name="checkboxlist" list="checkboxlistValues" key="Form.checkboxlist"/>
      <s:hidden name="hidden" key="Form.hidden"/>
      <s:submit key="Form.submitText" name="submitText"/>
    </s:form>
    <hr/>
 ...
  </body>
</html>
  • يبدأ النموذج في السطر 26 وينتهي في السطر 38. في السطر 26، السمة `action` مفقودة. تحدد هذه السمة الإجراء الذي سيتم إرسال بيانات النموذج إليه. إذا كانت السمة `action` مفقودة، فسيتم إرسال البيانات إلى الإجراء الذي تسبب في عرض العرض، وهو في هذه الحالة الإجراء `[Form]`.
  • السطر 27: حقل إدخال. تحدد السمة key تسميته. السمة name هي اسم المعلمة التي سيتم إرسالها. ستكون السلسلة المرسلة لهذا الحقل بالشكل التالي:

textfield=text_entered

يتوافق اسم المعلمة مع حقل في الإجراء [Form]. سيتم إدخال قيمة حقل الإدخال text_saisi في حقل textfield الخاص بالإجراء [Form]. وعلى العكس، إذا تم عرض طريقة العرض [Form.jsp] بعد الإجراء [Form]، فستكون قيمة حقل الإدخال هي قيمة حقل textfield في الإجراء [Form]. وبالتالي، يُستخدم هذا الحقل للقراءة والكتابة على حد سواء. يجب أن تحتوي فئة [Form] على طريقتي getTextField و setTextField لهذا الغرض.

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

  • السطر 28: إدخال كلمة المرور. ستكون السلسلة المرسلة إلى إجراء [Form] بالشكل

password=entered_password

يجب أن يوجد حقل كلمة مرور مع طرق get/set الخاصة به في إجراء [Form].

  • السطر 29: إدخال نص متعدد الأسطر في منطقة مكونة من 5 صفوف و40 عمودًا. ستكون السلسلة المرسلة إلى إجراء [Form] بالشكل التالي

textarea=input

يجب أن يوجد حقل textarea مع طرق get/set الخاصة به في إجراء [Form].

  • السطر 30: قائمة منسدلة ذات اختيار واحد (size=1). يمكن تحديد قيمة واحدة فقط من القائمة. ستكون السلسلة التي يتم إرسالها إلى الإجراء [Form] بالشكل التالي:

select1=selected_value

يجب أن يتواجد حقل select1 مع طرق get/set الخاصة به في الإجراء [Form].

السمة key هي تسمية القائمة. تحدد السمة list قائمة القيم التي سيتم وضعها في مربع القائمة المنسدلة. هنا، سيتم استدعاء طريقة getSelect1Values الخاصة بعملية [Form] لملء مربع القائمة المنسدلة. يمكن لهذه الطريقة إرجاع مجموعة أو قاموس أو مصفوفة. هنا، سترجع مصفوفة من السلاسل [string1, string2, ...] والتي ستولد كود HTML التالي:

<option value="string1">string1</option>

<option value="string2">string2</option>

...

سيتم عرض النص الموجود بين العلامات <option>...</option> في مربع القائمة المنسدلة. تحدد السمة value القيمة التي سيتم إرسالها إذا تم تحديد الخيار من قبل المستخدم. هنا، السمة value والنص المعروض في مربع القائمة المنسدلة متطابقان. في أغلب الأحيان، لا يكون هذا هو الحال. إذا تم تحديد الخيار الثاني، فسيتم إرسال السلسلة التالية إلى الإجراء [Form]:

select1=string2

سيتلقى حقل [Form].select1 عندئذٍ القيمة string2. وعلى العكس، إذا تم تعيين هذا الحقل على string3 عند عرض النموذج، فسيظهر العنصر الثالث بصريًا

<option value="string3">string3</option>

محددًا في مربع القائمة المنسدلة.

تحدد السمة headerValue النص الذي سيتم عرضه كخيار أول في مربع القائمة المنسدلة، وتحدد السمة headerKey القيمة التي سيتم إرسالها إذا تم تحديد هذا الخيار من قبل المستخدم.

  • السطر 31: مرة أخرى، قائمة بحجم 3 (size=3): على عكس القائمة السابقة حيث يظهر عنصر واحد فقط، ستظهر هنا 3 عناصر من القائمة. يظل وضع التحديد كما هو. يمكن تحديد عنصر واحد فقط.
  • السطر 32: قائمة أخرى، ولكن هذه المرة مع تحديد متعدد (multiple=true). في هذه الحالة، ستكون القيمة المرسلة إلى إجراء [Form] بالشكل التالي:

select3=string2&select3=string5

إذا تم تحديد الخيارين string2 و string5. عند إرسال قيم متعددة لنفس المعلمة — في هذه الحالة، select3 — يجب أن يحتوي الإجراء على حقل باسم المعلمة وأن يكون من النوع array. لذا، على سبيل المثال، يمكن أن يحتوي إجراء [Form] هنا على حقل مثل:

String[] select3;

والطرق get/set المقابلة. سيتلقى المصفوفة select3 بعد ذلك مصفوفة سلاسل الأحرف [string2, string5]. على العكس، عند عرض طريقة العرض [Form.jsp]، تحدد قيم حقل [Form].select3 الخيارات التي ستظهر محددة في قائمة select3. هذه عملية قراءة/كتابة دائمًا.

  • السطر 33: مجموعة من أزرار الاختيار. يتم توفير تسمية المجموعة بواسطة السمة key. يتم توفير تسميات الأزرار الفردية بواسطة السمة list. قيمة هذه السمة هي اسم طريقة في الإجراء [Form] يجب أن تُرجع مجموعة أو قاموسًا أو مصفوفة. هنا، ستُرجع الطريقة [Form].getRadioValues مصفوفة من السلاسل [string1, string2, ...] التي ستُنشئ علامات HTML التالية:

<input type="radio" name="radio" ... value="string1"/>string1

<input type="radio" name="radio" ... value="string2"/>string2

...

إذا تم تحديد زر الاختيار string2، فستكون القيمة المرسلة هي

radio=string2

وسيتم إدراج هذه القيمة في حقل [Form].radio.

  • السطر 34: مربع اختيار. إذا تم تحديده، فسيتم إرسال سلسلة المعلمة التالية إلى إجراء [Form]

checkbox=true

إذا لم يتم تحديد هذا الخيار، فلن يتم إرسال أي سلسلة معلمات. يحتوي Struts 2 على آلية داخلية تجعل كل شيء يسير كما لو أن السلسلة

checkbox=false

تم إرسالها.

  • السطر 35: قائمة بمربعات الاختيار. يمكن تحديد عدة مربعات اختيار. يتم توفير تسمية المجموعة بواسطة السمة key. يتم توفير تسميات مربعات الاختيار الفردية بواسطة السمة list. قيمة هذه السمة هي اسم طريقة في إجراء [Form] يجب أن تُرجع مجموعة أو قاموسًا أو مصفوفة. هنا، ستُرجع طريقة [Form].getCheckboxlistValues مصفوفة من السلاسل [string1, string2, ...] التي ستُنشئ علامات HTML التالية:

<input type="checkbox" name="checkboxlist" ... value="string1"/>string1

<input type="checkbox" name="checkboxlist" ... value="string2"/>string2

...

إذا حدد المستخدم مربعات الاختيار المسمّاة string2 و string5، فسيتم إرسال السلسلة التالية إلى إجراء [Form]:

checkboxlist=string2&checkboxlist=string5

سيتم تعيين المصفوفة [string2, string5] إلى حقل [Form].checkboxlist، والذي يجب أن يكون من نوع مصفوفة السلسلة، كما هو الحال مع قوائم التحديد المتعدد. وعلى العكس، عند عرض طريقة العرض [Form.jsp]، تحدد قيم حقل [Form].checkboxlist مربعات الاختيار التي سيتم تحديدها.

  • السطر 36: حقل مخفي. الحقل المخفي هو حقل يتم إرساله دون أن يقوم المستخدم بإدخاله. يتم تعيين قيمته بواسطة المبرمج. هنا، سيأخذ الحقل المخفي قيمته من حقل [Form].hidden. سيتم إرسال هذه القيمة نفسها في النموذج:

hidden=something

ويتم إعادة تعيينها إلى حقل [Form].hidden.

  • السطر 37: زر إرسال. سيؤدي النقر فوق هذا الزر إلى إرسال جميع قيم النموذج إلى الإجراء [Form]. ستبدو سلسلة المعلمات المرسلة كما يلي:

textfield=text&password=&textarea=line1%0D%0Aline2%0D%0A&select1=zero&select2=three&select3=zero&select3=two&__multiselect_select3=&radio=blue&checkbox=true&__checkbox_checkbox=true&checkboxlist=v%C3%A9lo&checkboxlist=bus&__multiselect_checkboxlist=&hidden=initial&submitText=Validate

9.5. إجراء [نموذج] – قسم الإدخال

فيما يلي الكود الخاص به:


package example;
 
import com.opensymphony.xwork2.ActionSupport;
 
public class Form extends ActionSupport {
 
  // constructor without parameters
  public Form() {
  }
  // form fields
  private String textfield = "texte";
  private String password = "secret";
  private String textarea = "ligne1\nligne2\n";
  private String select1 = "zéro";
  private String[] select1Values = new String[]{"zéro", "un", "deux"};
  private String select2 = "trois";
  private String[] select2Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
  private String[] select3 = new String[]{"zéro", "deux"};
  private String[] select3Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
  private String radio = "bleu";
  private String[] radioValues = new String[]{"bleu", "blanc", "rouge"};
  private Boolean checkbox = false;
  private String[] checkboxlist = new String[]{"vélo", "bus"};
  private String[] checkboxlistValues = new String[]{"voiture", "tram", "vélo", "bus", "métro"};
  private String hidden = "initial";
  private String submitText;
 
  // values selected in select3 field
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // values selected in checkboxlist field
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // utilitarian method
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }
 
// field getters and setters
  ....
}

  • السطر 1: الحقل المرتبط بعلامة textfield في النموذج. يُستخدم لتهيئة حقل الإدخال هذا وكذلك لاسترداد قيمته. يتلقى القيمة التي تم إدخالها في هذا الحقل عند طلب POST.
  • السطر 2: الحقل المرتبط بعلامة كلمة المرور في النموذج. يستقبل القيمة التي تم إدخالها في هذا الحقل أثناء طلب POST.
  • السطر 13: مرتبط بعلامة textarea في النموذج. يستقبل النص الذي تم إدخاله في هذا الحقل أثناء طلب POST.
  • السطر 14: مرتبط بعلامة `select1` في النموذج، وهي قائمة اختيار واحد. تحدد قيمته الأولية عنصرًا من القائمة المنسدلة. سيتم تحديد العنصر الذي يحتوي على سمة `value` مطابقة لهذه القيمة. عند إرسال POST، يستقبل سمة `value` للخيار المحدد في القائمة المنسدلة.
  • السطر 15: سلاسل الأحرف التي ستملأ القائمة المنسدلة select1.
  • السطران 16 و 17: نفس select1
  • السطر 18: مرتبط بعلامة select1 في النموذج، وهي قائمة اختيار متعددة. القيم الأولية في المصفوفة تحدد العناصر المقابلة في القائمة. سيتم تحديد العناصر التي تتطابق سمة value الخاصة بها مع إحدى هذه القيم. عند إرسال POST، يتلقى سمات value للخيارات المختلفة المحددة في القائمة المنسدلة.
  • السطر 19: سلاسل الأحرف التي ستملأ قائمة select3
  • السطر 20: مرتبط بعلامة radio في النموذج. تُستخدم قيمته لتحديد أحد أزرار الاختيار — وهو الزر الذي تتطابق سمة القيمة الخاصة به مع هذه القيمة. يستقبل قيمة زر الاختيار المحدد أثناء طلب POST.
  • السطر 21: التسميات والقيم المختلفة لأزرار الاختيار.
  • السطر 22: مرتبط بعلامة checkbox في النموذج. تُستخدم قيمته الأولية لتحديد المربع أو إلغاء تحديده. عند إرسال POST، يتلقى القيمة true إذا تم تحديد المربع، و false في حالة عدم تحديده.
  • السطر 23: مرتبط بعلامة قائمة مربعات الاختيار في النموذج. تحدد القيم الأولية في المصفوفة مربعات الاختيار المقابلة. سيتم تحديد مربعات الاختيار التي تتطابق سمة القيمة الخاصة بها مع إحدى هذه القيم الأولية. عند إرسال POST، يتلقى سمات القيمة لمربعات الاختيار المختلفة المحددة.
  • السطر 24: السلاسل التي ستملأ تسميات مربعات الاختيار في قائمة مربعات الاختيار.
  • السطر 25: مرتبط بالحقل المخفي. تحدد قيمته الأولية قيمة الحقل المخفي. عند إرسال POST، سيتلقى هذه القيمة نفسها لأن المستخدم لا يمكنه تعديلها.
  • السطر 26: مرتبط بعلامة الإرسال. أثناء طلب POST، يتلقى تسمية الزر. هذا الحقل اختياري.
  • سيتم مناقشة الطرق الواردة في الأسطر 29 و34 و39 بمزيد من التفصيل أدناه.

9.6. عرض [Form.jsp] – قسم التأكيد

تحتوي صفحة العرض [Form.jsp] على قسم "تأكيد الإدخالات" الذي لم يتم تناوله بعد:

Image

وفيما يلي كودها:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
...
    <hr/>
    <h2><s:text name="Confirmation.message"/></h2>
    <table border="1">
      <tr>
        <th><s:text name="Confirmation.champ"/></th>
        <th><s:text name="Confirmation.valeur"/></th>
      </tr>
      <tr>
        <td><s:text name="Form.textfield"/></td>
        <td><s:property value="textfield"/>
      </tr>
      <tr>
        <td><s:text name="Form.password"/></td>
        <td><s:property value="password"/>
      </tr>
      <tr>
        <td><s:text name="Form.textarea"/></td>
        <td><s:property value="textarea"/>
      </tr>
      <tr>
        <td><s:text name="Form.select1"/></td>
        <td><s:property value="select1"/>
      </tr>
      <tr>
        <td><s:text name="Form.select2"/></td>
        <td><s:property value="select2"/>
      </tr>
      <tr>
        <td><s:text name="Form.select3"/></td>
        <td><s:property value="select3SelectedValues"/>
      </tr>
      <tr>
        <td><s:text name="Form.radio"/></td>
        <td><s:property value="radio"/>
      </tr>
      <tr>
        <td><s:text name="Form.checkbox"/></td>
        <td><s:property value="checkbox"/>
      </tr>
      <tr>
        <td><s:text name="Form.checkboxlist"/></td>
        <td><s:property value="checkboxlistSelectedValues"/>
      </tr>
      <tr>
        <td><s:text name="Form.hidden"/></td>
        <td><s:property value="hidden"/>
      </tr>
    </table>
  </body>
</html>
  • السطر 13: يعرض قيمة حقل [Form].textfield
  • السطر 17: يعرض قيمة حقل [Form].password
  • السطر 21: يعرض قيمة حقل [Form].textarea
  • السطر 25: يعرض قيمة حقل [Form].select1
  • السطر 29: يعرض قيمة حقل [Form].select2
  • السطر 33: يعرض قيم مصفوفة [Form].select3. للقيام بذلك، يستخدم طريقة [Form].getSelect3SelectedValues.
  • السطر 37: يعرض قيمة حقل [Form].radio
  • السطر 41: يعرض قيمة حقل [Form].checkbox
  • السطر 45: يعرض قيم مصفوفة [Form].checkboxlist. للقيام بذلك، يستخدم الأسلوب [Form].getCheckboxlistSelectedValues.
  • السطر 49: يعرض قيمة حقل [Form].hidden

9.7. إجراء [Form] – قسم التأكيد

يتم تعريف الأسلوبين getSelect3SelectedValues و getCheckboxlistSelectedValues المذكورين أعلاه في فئة [Form]:


  // valeurs sélectionnées dans champ select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // valeurs sélectionnées dans champ checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // méthode utilitaire
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
}

إنها ببساطة تربط عناصر المصفوفات التي تحتاج إلى عرضها.

9.8. الاختبارات

عند تشغيل مشروع NetBeans، يتم عرض الصفحة الأولية التالية:

القيم المعروضة في [1] و[2] مستمدة من القيم الأولية للحقول في الإجراء [Form]. لنلقِ نظرة على بعض الأمثلة:


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

تشرح قيمة حقل textarea العروض [1A] و [2A].


private String[] select3 = new String[]{"zéro", "deux"};
private String[] select3Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};

تشرح قيمة حقل select3Values محتوى القائمة في [1B]. تشرح قيمة حقل select3 العناصر المحددة في [1B] والمعروضة في [2B].

إذا أدخلنا قيمًا أخرى في النموذج وأرسلناه، فسيتم إرسال القيم المدخلة إلى الحقول المقابلة في إجراء [Form]. بعد ذلك، سيتم إعادة عرض طريقة العرض [Form.jsp]. وبالتالي، نكون في نفس الموقف السابق، باستثناء أن القيم الأولية قد تم استبدالها بالقيم المرسلة. فيما يلي مثال على ذلك:

Image

Image

9.9. إجراء [Form1] وعرض [Form1.jsp]

يقوم ملف التكوين [example.xml] بتكوين الإجراء [Form1] التالي:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
 
<struts>
  <package name="example" namespace="/example" extends="struts-default">
    <action name="Form" class="example.Form">
      <result name="success">/example/Form.jsp</result>
    </action>
    <action name="Form1" class="example.Form1">
      <result name="success">/example/Form1.jsp</result>
    </action>
    <action name="Form2" class="example.Form2">
      <result name="success">/example/Form2.jsp</result>
    </action>
  </package>
</struts>

الأسطر 11–13: يؤدي استدعاء الإجراء [Form1] إلى إنشاء عرض [Form1.jsp].

يكون الإجراء [Form1] كما يلي:


package example;
 
import com.opensymphony.xwork2.ActionSupport;
 
public class Form1 extends ActionSupport{
 
  // constructor without parameters
  public Form1() {
  }
  // form fields
  private Data data=new Data();
 
  /**
   * @return the data
   */
  public Data getData() {
    return data;
  }
 
  /**
   * @param data the data to set
   */
  public void setData(Data data) {
    this.data = data;
  }
}

تتطابق الإجراء [Form1] مع الإجراء [Form]، باستثناء أن جزء النموذج الخاص به قد تم نقله إلى فئة منفصلة، وهي فئة [Data]، في السطر 11. فئة [Data] هي كما يلي:


package example;
 
public class Data {
 
  // constructor without parameters
  public Data() {
  }
  
// form fields
  private String textfield = "texte";
  private String password = "secret";
  private String textarea = "ligne1\nligne2\n";
  private String select1 = "zéro";
  private String[] select1Values = new String[]{"zéro", "un", "deux"};
  private String select2 = "trois";
  private String[] select2Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
  private String[] select3 = new String[]{"zéro", "deux"};
  private String[] select3Values = new String[]{"zéro", "un", "deux", "trois", "quatre"};
  private String radio="bleu";
  private String[] radioValues = new String[]{"bleu", "blanc", "rouge"};
  private Boolean checkbox = false;
  private String[] checkboxlist = new String[]{"vélo", "bus"};
  private String[] checkboxlistValues = new String[]{"voiture", "tram", "vélo", "bus", "métro"};
  private String hidden = "initial";
  private String submitText;
 
  // values selected in select3 field
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }
 
  // values selected in checkboxlist field
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }
 
  // utilitarian method
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }
 
  // getters and setters
....
 
}

نجد الحقول التي تم إعلانها مسبقًا في الإجراء [Form].

من المنطقي تمامًا أن تكون طريقة العرض [Form1.jsp] مشابهة لطريقة العرض [Form.jsp]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Form.titre"/></title>
    <s:head/>
  </head>
 
  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="Form.message"/></h2>
    <h3><s:text name="Form.langues"/></h3>
    <ul>
      <li>
        <s:url id="url" action="Form">
          <s:param name="request_locale">en</s:param>
        </s:url>
        <s:a href="%{url}"><s:text name="Form.anglais"/></s:a>
      </li>
      <li>
        <s:url id="url" action="Form">
          <s:param name="request_locale">fr</s:param>
        </s:url>
        <s:a href="%{url}"><s:text name="Form.francais"/></s:a>
      </li>
    </ul>
    <s:form name="formulaire">
      <s:textfield name="data.textfield" key="Form.textfield" />
      <s:password name="data.password" key="Form.password"/>
      <s:textarea name="data.textarea" key="Form.textarea" cols="40" rows="5"/>
      <s:select name="data.select1" list="data.select1Values" size="1" key="Form.select1" headerValue="<-- select 1 -->" headerKey="-1" />
      <s:select name="data.select2" size="3" list="data.select2Values" key="Form.select2"/>
      <s:select name="data.select3" size="3" list="data.select3Values" key="Form.select3" multiple="true"/>
      <s:radio name="data.radio" list="data.radioValues" key="Form.radio"/>
      <s:checkbox name="data.checkbox" key="Form.checkbox"/>
      <s:checkboxlist name="data.checkboxlist" list="data.checkboxlistValues" key="Form.checkboxlist"/>
      <s:hidden name="data.hidden" key="Form.hidden"/>
      <s:submit key="Form.submitText" name="data.submitText"/>
    </s:form>
    <hr/>
    <h2><s:text name="Confirmation.message"/></h2>
    <table border="1">
      <tr>
        <th><s:text name="Confirmation.champ"/></th>
        <th><s:text name="Confirmation.valeur"/></th>
      </tr>
      <tr>
        <td><s:text name="Form.textfield"/></td>
        <td><s:property value="data.textfield"/>
      </tr>
      <tr>
        <td><s:text name="Form.password"/></td>
        <td><s:property value="data.password"/>
      </tr>
      <tr>
        <td><s:text name="Form.textarea"/></td>
        <td><s:property value="data.textarea"/>
      </tr>
      <tr>
        <td><s:text name="Form.select1"/></td>
        <td><s:property value="data.select1"/>
      </tr>
      <tr>
        <td><s:text name="Form.select2"/></td>
        <td><s:property value="data.select2"/>
      </tr>
      <tr>
        <td><s:text name="Form.select3"/></td>
        <td><s:property value="data.select3SelectedValues"/>
      </tr>
      <tr>
        <td><s:text name="Form.radio"/></td>
        <td><s:property value="data.radio"/>
      </tr>
      <tr>
        <td><s:text name="Form.checkbox"/></td>
        <td><s:property value="data.checkbox"/>
      </tr>
      <tr>
        <td><s:text name="Form.checkboxlist"/></td>
        <td><s:property value="data.checkboxlistSelectedValues"/>
      </tr>
      <tr>
        <td><s:text name="Form.hidden"/></td>
        <td><s:property value="data.hidden"/>
      </tr>
    </table>
  </body>
</html>

يمكن توضيح الفرق بين عرضي [Form] و [Form1] من خلال السطر 27 الموضح أدناه:


<s:textfield name="data.textfield" key="Form.textfield" />

يتم ربط حقل الإدخال للقراءة والكتابة بحقل data.textfield الخاص بعملية [Form1]. تذكر أنه بعد التنفيذ، تصبح خصائص عملية [Form1] متاحة للعرض. وبالتالي، سيتم تقييم سمة الاسم السابقة على أنها [Form1].getData().getTextField(). ولذلك، يجب أن تكون طريقتا get السابقتان موجودتين.

9.10. الإجراء [Form2] وعرض [Form2.jsp]

يقوم ملف التكوين [example.xml] بتكوين الإجراء [Form2] التالي:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
 
<struts>
  <package name="example" namespace="/example" extends="struts-default">
    <action name="Form" class="example.Form">
      <result name="success">/example/Form.jsp</result>
    </action>
    <action name="Form1" class="example.Form1">
      <result name="success">/example/Form1.jsp</result>
    </action>
    <action name="Form2" class="example.Form2">
      <result name="success">/example/Form2.jsp</result>
    </action>
  </package>
</struts>

الأسطر 14–16: يؤدي استدعاء الإجراء [Form2] إلى إنشاء عرض [Form2.jsp].

يكون الإجراء [Form2] كما يلي:


package example;
 
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class Form2 extends ActionSupport implements ModelDriven {
 
  // constructor without parameters
  public Form2() {
  }
  // action model
  public Object getModel() {
    return new Data();
  }
}
  • السطر 6: يقوم الإجراء [Form2] بتنفيذ واجهة [ModelDriven]. هناك طريقة واحدة فقط يجب تنفيذها: طريقة getModel في السطر 12. تُرجع هذه الطريقة مثيلًا لفئة [Data] التي تمت مناقشتها سابقًا.

إن حقيقة أن الإجراء [Form2] ينفذ واجهة [ModelDriven] لها النتيجة التالية: العرض المعروض بعد الإجراء [Form2] له وصول مباشر إلى خصائص نموذج الإجراء [Form2]، والتي يتم إرجاعها بواسطة الطريقة getModel(). وبالتالي، يعود العرض [Form2.jsp] إلى ما كان عليه مع الإجراء [Form] الأولي:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Form.titre"/></title>
    <s:head/>
  </head>
 
  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="Form.message"/></h2>
    <h3><s:text name="Form.langues"/></h3>
    <ul>
      <li>
        <s:url id="url" action="Form">
          <s:param name="request_locale">en</s:param>
        </s:url>
        <s:a href="%{url}"><s:text name="Form.anglais"/></s:a>
      </li>
      <li>
        <s:url id="url" action="Form">
          <s:param name="request_locale">fr</s:param>
        </s:url>
        <s:a href="%{url}"><s:text name="Form.francais"/></s:a>
      </li>
    </ul>
    <s:form name="formulaire">
      <s:textfield name="textfield" key="Form.textfield" />
      <s:password name="password" key="Form.password"/>
      <s:textarea name="textarea" key="Form.textarea" cols="40" rows="5"/>
      <s:select name="select1" list="select1Values" size="1" key="Form.select1" headerValue="<-- select 1 -->" headerKey="-1" />
      <s:select name="select2" size="3" list="select2Values" key="Form.select2"/>
      <s:select name="select3" size="3" list="select3Values" key="Form.select3" multiple="true"/>
      <s:radio name="radio" list="radioValues" key="Form.radio"/>
      <s:checkbox name="checkbox" key="Form.checkbox"/>
      <s:checkboxlist name="checkboxlist" list="checkboxlistValues" key="Form.checkboxlist"/>
      <s:hidden name="hidden" key="Form.hidden"/>
      <s:submit key="Form.submitText" name="submitText"/>
    </s:form>
    <hr/>
    <h2><s:text name="Confirmation.message"/></h2>
    <table border="1">
      <tr>
        <th><s:text name="Confirmation.champ"/></th>
        <th><s:text name="Confirmation.valeur"/></th>
      </tr>
      <tr>
        <td><s:text name="Form.textfield"/></td>
        <td><s:property value="textfield"/>
      </tr>
      <tr>
        <td><s:text name="Form.password"/></td>
        <td><s:property value="password"/>
      </tr>
      <tr>
        <td><s:text name="Form.textarea"/></td>
        <td><s:property value="textarea"/>
      </tr>
      <tr>
        <td><s:text name="Form.select1"/></td>
        <td><s:property value="select1"/>
      </tr>
      <tr>
        <td><s:text name="Form.select2"/></td>
        <td><s:property value="select2"/>
      </tr>
      <tr>
        <td><s:text name="Form.select3"/></td>
        <td><s:property value="select3SelectedValues"/>
      </tr>
      <tr>
        <td><s:text name="Form.radio"/></td>
        <td><s:property value="radio"/>
      </tr>
      <tr>
        <td><s:text name="Form.checkbox"/></td>
        <td><s:property value="checkbox"/>
      </tr>
      <tr>
        <td><s:text name="Form.checkboxlist"/></td>
        <td><s:property value="checkboxlistSelectedValues"/>
      </tr>
      <tr>
        <td><s:text name="Form.hidden"/></td>
        <td><s:property value="hidden"/>
      </tr>
    </table>
  </body>
</html>

إذن، السطر 27، الذي كان سابقًا:


<s:textfield name="data.textfield" key="Form.textfield" />

يصبح:


<s:textfield name="textfield" key="Form.textfield" />

تتمثل ميزة وضع النموذج خارج العرض في أن هذه البنية تحدد بشكل أوضح وظائف كل عنصر من عناصر بنية MVC. يتم تحديد النموذج M بوضوح من خلال فئة. علاوة على ذلك، قد يكون النموذج موجودًا قبل الإجراءات. وبالتالي، إذا كان تطبيق ويب يدير قاعدة بيانات للأشخاص، فمن المرجح أن تكون فئة Person موجودة. إذا كانت إحدى الإجراءات توفر نموذج إدخال لإنشاء شخص جديد، فإن النموذج الخاص بهذه الإجراء يكون واضحًا: وهو فئة Person.

لقد رأينا مدى سهولة التبديل من الإجراء [Form] إلى الإجراء [Form2]:

  • تم تغليف النموذج M للعرض V في فئة منفصلة
  • الإجراء الذي يولد العرض باستخدام هذا النموذج ينفذ واجهة ModelDriven باستخدام طريقة getModel، التي تُرجع مثيلًا للنموذج M. يمكن بعد ذلك النظر إلى الإجراء على أنه امتداد لوحدة التحكم C في Struts 2، وهي فئة FilterDispatcher.

يمكن للقارئ تطبيق هذا النهج على أي إجراء موصوف أدناه.