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: الحزمة default. تحدد عنوان إعادة التوجيه لعنوان 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=النص_المُدخل

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

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

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

password=كلمة_المرور_المدخلة

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

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

textarea=saisie

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

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

select1=القيمة_المختارة

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

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

<option value="chaine1">chaine1</option>

<option value="chaine2">chaine2</option>

...

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

select1=chaine2

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

<option value="chaine3">chaine3</option>

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

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

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

select3=سلسلة2&select3=سلسلة5

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

String[] select3؛

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

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

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

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

...

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

radio=chaine2

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

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

checkbox=true

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

checkbox=false

قد تم إرسالها.

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

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

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

...

إذا قام المستخدم بتحديد المربعات التي تحمل العناوين chaine2 و chaine5، فسيتم إرسال السلسلة التالية إلى الإجراء [Form]:

checkboxlist=sلسلة2&checkboxlist=sلسلة5

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

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

hidden=شيء ما

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

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

textfield=نص&password=&textarea=السطر1%0D%0Aالسطر2%0D%0A&select1=صفر&select2=ثلاثة&select3=صفر&select3=اثنان&__multiselect_select3=&radio=أزرق&checkbox=صحيح&__checkbox_checkbox=صحيح&checkboxlist=دراجة&checkboxlist=حافلة&__multiselect_checkboxlist=&hidden=أولي&submitText=تأكيد

9.5. الإجراء [Form] – جزء الإدخال

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


package example;

import com.opensymphony.xwork2.ActionSupport;

public class Form extends ActionSupport {

  // منشئ بدون معلمات
  public Form() {
  }
  // حقول النموذج
  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;

  // القيم المحددة في حقل select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // القيم المحددة في حقل قائمة مربعات الاختيار
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // طريقة مساعدة
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }

// دالات الحصول على القيم وتعيينها للحقول
  ....
}
  • السطر 1: الحقل المرتبط بعلامة textfield في النموذج. سيُستخدم لتهيئة حقل الإدخال هذا وكذلك لاسترداد قيمته. يتلقى عند تنفيذ POST، القيمة التي تم إدخالها في هذا الحقل.
  • السطر 2: الحقل المرتبط بعلامة password في النموذج. يتلقى عند تنفيذ POST القيمة التي تم إدخالها في هذا الحقل.
  • السطر 13: مرتبط بعلامة textarea في النموذج. يستقبل عند POST النص الذي تم إدخاله في هذا الحقل.
  • السطر 14: مرتبط بعلامة select1 في النموذج، وهي قائمة ذات خيار واحد. تحدد قيمته الأولية عنصرًا من القائمة المنسدلة. وسيتم تحديد العنصر الذي يكون سمة value الخاصة به مساوية لهذه القيمة. عند تنفيذ POST، يتلقى السمة value من الخيار المحدد في القائمة المنسدلة.
  • السطر 15: سلاسل الأحرف التي ستُستخدم لتغذية القائمة المنسدلة select1.
  • السطران 16 و17: نفس الشيء بالنسبة لـ select1
  • السطر 18: مرتبط بعلامة select1 في النموذج، وهي قائمة متعددة الخيارات. القيم الأولية في الجدول تحدد العناصر المقابلة في القائمة. وسيتم تحديد العناصر التي يكون سمة value الخاصة بها مساوية لأحد هذه العناصر. عند تنفيذ POST، يتم استلام سمات value الخاصة بالخيارات المختلفة المحددة في القائمة المنسدلة.
  • السطر 19: سلاسل الأحرف التي ستُضاف إلى القائمة select3
  • السطر 20: مرتبط بعلامة radio في النموذج. تُستخدم قيمته لتحديد أحد أزرار الاختيار، وهو الزر الذي تكون قيمة سمة value الخاصة به مساوية لهذه القيمة. تتلقى عند POST، قيمة زر الاختيار الذي تم تحديده.
  • السطر 21: العناوين والقيم المختلفة لأزرار الاختيار.
  • السطر 22: مرتبط بعلامة checkbox في النموذج. تُستخدم قيمته الأولية لتحديد/إلغاء تحديد المربع. يتلقى عند وقت POST القيمة true إذا تم تحديد المربع، و false في حالة عدم تحديده.
  • السطر 23: مرتبط بعلامة checkboxlist في النموذج. القيم الأولية في الجدول تحدد خانات الاختيار المقابلة. وسيتم تحديد الخانات التي يكون سمة value الخاصة بها مساوية لإحدى هذه القيم الأولية. عند تنفيذ POST، يتم استلام سمات value الخاصة بمربعات الاختيار المختلفة التي تم تحديدها.
  • السطر 24: سلاسل الأحرف التي ستُستخدم في صياغة تسميات خانات checkboxlist.
  • السطر 25: مرتبط بالحقل المخفي hidden. ستحدد قيمته الأولية قيمة الحقل المخفي. عند POST، سيتلقى هذه القيمة نفسها لأن المستخدم لا يملك إمكانية تعديلها.
  • السطر 26: مرتبط بالعلامة submit. عند تنفيذ 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]:


  // القيم المحددة في الحقل select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // القيم المحددة في حقل checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // طريقة مساعدة
  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{

  // منشئ بدون معلمات
  public Form1() {
  }
  // حقول النموذج
  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 {

  // منشئ بدون معلمات
  public Data() {
  }
  
// حقول النموذج
  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;

  // القيم المحددة في حقل select3
  public String getSelect3SelectedValues() {
    return getValue(select3);
  }

  // القيم المحددة في حقل checkboxlist
  public String getCheckboxlistSelectedValues() {
    return getValue(checkboxlist);
  }

  // طريقة مساعدة
  public String getValue(String[] values) {
    String result = "";
    for (String value : values) {
      result += " " + value;
    }
    return result;
  }

  // أدوات الاسترجاع والتعيين
....

}

ونجد الحقول التي تم الإعلان عنها سابقًا في الإجراء [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] متاحة للعرض. وبالتالي، سيتم تقييم السمة name السابقة على أنها [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 {

  // منشئ بدون معلمات
  public Form2() {
  }
  // نموذج الإجراء
  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 بوضوح من خلال فئة. علاوة على ذلك، يمكن أن يكون النموذج موجودًا قبل الإجراءات. وهكذا، إذا كان تطبيق ويب يدير قاعدة بيانات للأشخاص، فمن المحتمل أن تكون الفئة Personne موجودة بالفعل. وإذا كانت إحدى الإجراءات توفر نموذج إدخال لإنشاء شخص جديد، فإن نموذج هذه الإجراء يكون جاهزًا بالفعل: وهو الفئة Personne.

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

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

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