6. النماذج HTML
حتى الآن، استخدمنا نموذجًا واحدًا لا يتضمن سوى حقلين للإدخال. ونقترح هنا إنشاء ومعالجة نموذج باستخدام المكونات الرسومية المعتادة (أزرار الاختيار، مربعات الاختيار، حقول الإدخال، مربعات القائمة المنسدلة، القوائم).
6.1. طرق عرض التطبيق
سيحتوي التطبيق على عرضين فقط. يعرض الأول نموذجًا فارغًا:
QZXW2HTMLP000547ZQX 1 - formulaire

ستسمح لنا هذه الواجهة الأولى، المسماة formulaire.jsp، بتطبيق علامات مختلفة من مكتبة struts-html. يقوم المستخدم بملء النموذج:

يتيح الزر [Envoyer] الحصول على تأكيد للقيم التي تم إدخالها. وستكون هذه هي الشاشة الثانية:

ستسمح لنا هذه الصفحة الثانية باستخدام مكتبتين أخريين من العلامات هما struts-bean وstruts-logic. يتيح لنا الرابط [Retour au formulaire] العودة إلى النموذج كما تم ملؤه. عندها نعود إلى الصفحة الأولى.
6.2. بنية التطبيق
![]() |
- سيتم تمثيل النموذج (الطريقة 1) بواسطة كائن Struts ديناميكي يُسمى dynaFormulaire من نوع مشتق من DynaActionForm. وسيتم عرضه بواسطة طريقة العرض formulaire.jsp.
- وستكون الغاية من إجراء Struts InitFormulaireAction هي الحصول على البيانات اللازمة لعرض النموذج
- وسيتم معالجة النموذج المملوء بواسطة الإجراء ForwardAction الذي سيقوم بإعادة توجيه الطلب إلى العرض الثاني confirmation.jsp. وسيتولى هذا العرض عرض قيم النموذج.
6.3. تكوين التطبيق
6.3.1. الملف server.xml
سيُسمى سياق التطبيق /formulaire2. لذا سنضيف السطر التالي إلى ملف server.xml في Tomcat:
بعد ذلك، نعيد تشغيل Tomcat حتى يأخذ السياق الجديد في الاعتبار. يمكننا التحقق من صحة هذا السياق عن طريق طلب URL http://localhost:8080/formulaire2:

6.3.2. الملف web.xml
سيكون ملف التكوين web.xml للتطبيق كما يلي:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>
بالنسبة لملفات التكوين web.xml التي سبق أن تناولناها، نقوم بإجراء بعض التعديلات:
- نقوم بإدخال مكتبتين جديدتين للعلامات هما struts-bean و struts-logic. وسيتم استخدامهما في العرض confirmation.jsp. أما العرض formulaire.jsp فسيستخدم مكتبة struts-html.
6.3.3. الملف struts-config.xml
أما الملف struts-config.xml فسيكون كما يلي:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources
parameter="ApplicationResources"
null="false"/>
</struts-config>
تتضمن هذه الصفحة الأقسام الثلاثة الرئيسية التالية:
- تحديد النماذج في القسم <form-beans>
- إعلان الإجراءات في القسم <action-mappings>
- إعلان ملف الموارد في <message-ressources>
6.3.4. كائنات (beans) النماذج في التطبيق
الكائنات المستخدمة لتمثيل نماذج HTML في التطبيق هي كائنات من النوع ActionForm أو مشتقات منها (DynaActionForm، DynaValidatorForm، ...). يُطلق عليها اسم «بيانات» لأن بنيتها تتبع قواعد JavaBeans. لا يوجد سوى «بيانات» نموذج واحد في تطبيقنا، يُسمى dynaFormulaire وهو من النوع المشتق من DynaActionForm. وسيُستخدم في الحالات التالية:
- احتواء البيانات اللازمة لعرض العرض رقم 1
- استرداد قيم النموذج من العرض رقم 1 عندما يقوم المستخدم بتقديمه (submit)
- احتواء البيانات اللازمة لعرض العرض رقم 2
ترتبط بنية البين dynaFormulaire ارتباطًا وثيقًا بنموذج العرض رقم 1. دعونا ندرس هذا النموذج:
![]() |
رقم | النوع HTML | الدور |
<input name="opt" type="radio" value="نعم"> <input name="opt" type="radio" value="لا"> | مجموعة من أزرار الاختيار المرتبطة ببعضها (نفس الاسم) | |
<input name="chk1" type="radio" value="on"> <input name="chk2" type="radio" value="on"> <input name="chk3" type="radio" value="on"> | مجموعات من مربعات الاختيار مستقلة (ليست بنفس الاسم) | |
<input type="text " name="champSaisie" > | حقل إدخال | |
<input type="password " name="mdp" > | حقل كلمة المرور | |
<textarea name="boiteSaisie">...</textarea> | حقل إدخال متعدد الأسطر | |
<select name="combo" size="1">..</select> | قائمة منسدلة | |
<select name="listeSimple" size="3">..</select> | قائمة للاختيار الفردي | |
<select name="listeMultiple" size="3" multiple>..</select> | قائمة متعددة الخيارات | |
<input type="button" value="مسح" onclick='effacerListe("listeSimple")'> | زر يسمح بإلغاء تحديد العناصر المحددة في listeSimple (7) | |
<input type="button" value="مسح" onclick='effacerListe("listeMultiple")'> | زر يسمح بإلغاء تحديد العناصر المحددة في listeMultiple (8) | |
<input type="submit" value="إرسال"> | زر إرسال النموذج | |
<input type="hidden" name="secret" value="..."> | حقل مخفي |
لنميز بين عدة حالات:
- يُستخدم الكائن dynaFormulaire لاحتواء قيم النموذج HTML أعلاه الذي سيتم إرساله بواسطة الزر [Envoyer]. لذلك يجب أن يحتوي على نفس الحقول الموجودة في النموذج HTML. يتم تحديد نوع الحقل وفقًا للقاعدة التالية:
- إذا كان الحقل HTML لا يوفر سوى قيمة واحدة، فسيكون نوع الحقل في dynaFormulaire هو java.lang.String
- إذا كان الحقل HTML يقدم عدة قيم، فإن الحقل في dynaFormulaire سيكون من النوع java.lang.String[]
في النموذج HTML أعلاه، الحقل listeMultiple هو الوحيد الذي يمكن أن يرتبط بقيم متعددة (تلك التي يختارها المستخدم). لذا، فإن التعريف الأول للكائن dynaFormulaire سيكون كما يلي:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
</form-bean>
كيف سيتم ملء dynaFormulaire بالقيم الواردة في النموذج HTML المرسلة من عميل الويب؟
سيتلقى الحقل opt القيمة "نعم" إذا تم تحديد الحقل HTML <input type="radio" name="opt" value="نعم">، والقيمة "لا" إذا تم تحديد الحقل <input type="radio" name="opt" value="لا">. | |
سيحصل الحقل chk1 على القيمة "on" إذا تم تحديد الحقل HTML <input name="chk1" type="radio" value="1">، وإلا فلن يحصل على أي قيمة. في هذه الحالة الأخيرة، سيحتفظ الحقل chk1 بقيمته السابقة. | |
نفس الشيء | |
كما سبق | |
سيتلقى الحقل champSaisie النص الذي أدخله المستخدم في الحقل HTML <input type="text" name="champSaisie">. قد يكون هذا النص سلسلة فارغة. | |
سيتلقى الحقل mdp النص الذي أدخله المستخدم في الحقل HTML <input type="password" name="mdp">. وقد يكون هذا النص سلسلة فارغة. | |
سيتلقى الحقل boiteSaisie النص الذي أدخله المستخدم في الحقل HTML <textarea name="boiteSaisie">...</textarea>. يشكل هذا النص سلسلة أحرف واحدة، تتكون من الأسطر التي كتبها المستخدم، مفصولة عن بعضها البعض بسلسلة الأحرف "\r\n". وقد يكون النص الناتج سلسلة فارغة. | |
سيتلقى حقل القائمة المنسدلة الخيار الذي يختاره المستخدم في الحقل HTML <select name="combo" size="1">..</select>. الخيار المحدد هو الذي يظهر في القائمة المنسدلة. إذا كان الخيار المحدد HTML من النوع <option value="XX">YY</option>، فسيتلقى حقل القائمة المنسدلة القيمة "XX". إذا كان الخيار المحدد HTML من النوع <option>YY</option>، فسيتلقى حقل القائمة المنسدلة القيمة "YY". | |
سيتلقى الحقل listeSimple الخيار الذي حدده المستخدم في الحقل HTML <select name="listeSimple" size="..">..</select> إن وجد. وإذا لم تكن هناك أي خيار، فلن يتلقى الحقل listeSimple أي قيمة وسيحتفظ بقيمته السابقة. وتتبع القيمة المخصصة فعليًّا للحقل listeSimple القواعد المحددة لقائمة الاختيار. | |
سيتلقى الحقل listeMultiple من النوع String[] الخيارات التي اختارها المستخدم في الحقل HTML <select name="listeMultiple" size=".." multiple>..</select> إن وجدت. وإذا لم تكن هناك خيارات، فلن يتلقى المصفوفة listeMultiple أي قيم وسيظل محتواها دون تغيير. تتبع القيم المخصصة فعليًّا للمصفوفة listeMultiple القواعد المحددة لقائمة الاختيار. | |
سيتلقى الحقل السري القيمة XX من الحقل HTML <input type="hidden" name="secret" value="XX">. قد يكون هذا النص سلسلة فارغة. |
- يُستخدم الكائن dynaFormulaire لتعيين المحتوى الأولي للعرض رقم 1. وستُستخدم قيم الحقول السابقة للأغراض التالية:
يجب أن تكون قيمته «نعم» أو «لا» حتى يتمكن المتصفح من تحديد زر الاختيار الذي يجب تحديده | |
إذا كانت قيمة chk1 هي «on»، فسيتم تحديد خانة الاختيار، وإلا فلن يتم تحديدها | |
نفس الشيء | |
نفس الشيء | |
سيتم عرض قيمة الحقل في منطقة الإدخال champSaisie | |
سيتم عرض قيمة الحقل في منطقة الإدخال mdp | |
سيتم عرض قيمة الحقل في منطقة الإدخال boiteSaisie | |
تشير قيمة هذا الحقل إلى العنصر الذي يجب تحديده في القائمة المنسدلة عند عرض النموذج | |
كما سبق | |
تشير قيم الجدول listeMultiple إلى العناصر التي يجب تحديدها من القائمة المتعددة عند عرض النموذج | |
سيتم تعيين قيمة الحقل إلى السمة value للحقل السري HTML. |
تحتاج طريقة العرض رقم 1 إلى معلومات أخرى:
- قائمة القيم المراد عرضها في القائمة المنسدلة
- قائمة القيم المراد عرضها في القائمة listeSimple
- قائمة القيم المراد عرضها في القائمة listeMultiple
هناك عدة طرق لتزويد العرض بهذه المعلومات. على سبيل المثال، يمكن استخدام الجداول الموجودة في الاستعلام الذي يتم تمريره إلى العرض. هنا، نضع هذه الجداول في الكائن dynaFormulaire:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
سيتم تهيئة النموذج dynaFormulaire بواسطة الإجراء /init الذي سيستدعي كائنًا مشتقًا من Action يُسمى InitFormulaireAction. وسيتولى هذا الكائن إنشاء الجداول الثلاثة اللازمة لعرض القوائم الثلاث ووضعها في البين dynaFormulaire. يمنح ملف التكوين هذا الكائن نطاقًا يساوي مدة الجلسة. وهذا يعني أن وحدة التحكم Struts ستضع هذا الكائن في الجلسة. وبالتالي لن نحتاج إلى إعادة إنشائه بين دورتي الطلب والاستجابة. كما أن الإجراء /init لن يتم استدعاؤه سوى مرة واحدة.
- يُستخدم الكائن dynaFormulaire أيضًا لتوفير محتوى العرض رقم 2. ويقتصر هذا العرض على عرض القيم الموجودة فيه.
6.3.5. إجراءات التطبيق
يتم تنفيذ الإجراءات بواسطة كائنات من نوع Action أو مشتقات منه. يتم تكوين الإجراءات داخل العلامات <action-mappings>:
<action-mappings>
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
تجدر الإشارة إلى أنه لا يوجد دائمًا نموذج مرتبط بكل إجراء. وهذا هو الحال، في المثال أعلاه، بالنسبة للإجراء /affiche. قبل تفصيل كل إجراء، دعونا نستعرض كيفية عمل الثنائي "الإجراء-النموذج" المرتبط داخل علامة <action>:
- تبدأ العملية بطلب من عميل الويب وتنتهي بإرسال صفحة استجابة. وهذا هو دورة الطلب والاستجابة بين العميل وخادم الويب. يتم استلام الطلب بواسطة وحدة التحكم Struts من النوع ActionServlet أو أحد مشتقاتها. كما أن وحدة التحكم هذه هي التي ترسل الاستجابة.
- يتم إنشاء «بيان» النموذج من النوع ActionForm أو أحد مشتقاته إذا لم يكن موجودًا بالفعل. يتحقق وحدة التحكم مما إذا كان بإمكانها العثور على كائن باسم name في النطاق المحدد بواسطة scope. إذا كان الأمر كذلك، فإنها تستخدمه. وإذا لم يكن كذلك، فإنها تنشئه وتضعه في النطاق المحدد بواسطة scope، مرتبطًا بالسمة المحددة بواسطة name.
في مثال الإجراء /init على سبيل المثال، سيقوم وحدة التحكم بتنفيذ request.getSession().getAttribute("dynaFormulaire") لمعرفة ما إذا كان dynaFormulaire قد تم إنشاؤه بالفعل أم لا. وإذا لم يكن الأمر كذلك، فستقوم بإنشائه وإدراجه في الجلسة باستخدام تعليمة من نوع request.getSession().setAttribute("dynaFormulaire", new DynaFormulaire(...)).
- كما سيبحث المتحكم عن كائن Action من النوع المحدد بواسطة السمة type. إذا لم يجده، فسيقوم بإنشائه، وإلا فسيستخدمه.
- سيتم استدعاء طريقة reset الخاصة بـ bean النموذج. ويتم إعادة استخدام هذا bean، باستثناء حالة إنشائه الأولي. وعندها يحتوي على بيانات قد نرغب في «تنظيفها». ويتم القيام بذلك في طريقة reset الخاصة بـ bean ActionForm أو أحد مشتقاته.
- إذا كانت العملية هي الهدف لنموذج تم إرساله عبر POST، فإن قيم النموذج الموجودة في طلب العميل تُنسخ إلى الحقول التي تحمل نفس الاسم في كائن النموذج. وتجدر الإشارة إلى أن الطريقة reset قد تم استدعاؤها قبل عملية النسخ هذه.
- إذا حددت التهيئة السمة validate="true"، فسيتم استدعاء الطريقة validate الخاصة بـ bean النموذج. وعندئذٍ يجب أن تتحقق هذه الطريقة من صحة بيانات bean. وغالبًا ما يتم هذا التحقق فقط عندما يتلقى النموذج بيانات جديدة عبر نموذج تم إرساله عبر البوست، ويكون الهدف هو التحقق من صحة هذه البيانات. وتقوم هذه الطريقة بإرجاع قائمة محتملة بالأخطاء إلى وحدة التحكم في كائن ActionErrors.
- إذا كان الكائن ActionErrors غير فارغ، يقوم وحدة التحكم بعرض العرض المحدد بواسطة السمة input الخاصة بالإجراء.
- إذا لم يُطلب التحقق من صحة البيانات أو إذا تم التحقق بنجاح، يقوم وحدة التحكم بتنفيذ الطريقة execute للكائن من نوع Action أو مشتق منه المرتبط بالإجراء الجاري. ويتم معالجة طلب عميل الويب في هذه الطريقة. ترجع الطريقة execute كائنًا من نوع ActionForward مفهرسًا بمفاتيح من نوع سلسلة أحرف. هذه المفاتيح هي تلك المُعلنة بواسطة علامات forward الخاصة بالإجراء المُهيأ. في مثالنا، يحتوي الإجراء /init على علامة forward واحدة. وهي تربط المفتاح "afficherFormulaire" بالعرض formulaire.jsp.
- يقوم وحدة التحكم بعرض العرض المرتبط بالمفتاح المستلم. قد يكون هذا العرض في الواقع إجراءً، وفي هذه الحالة تتكرر العملية السابقة.
الإجراء /init
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
- يتم تنفيذ الإجراء /init عادةً مرة واحدة خلال الدورة الأولى من الطلب والاستجابة عندما يطلب المستخدم URL http://localhost:8080/formulaire2/init.do
- يتم إنشاء الكائن dynaFormulaire أو إعادة تدويره. يتم استرداده (إعادة التدوير) أو وضعه (الإنشاء) في الجلسة وفقًا لما يحدده السمة scope.
- يتم استدعاء طريقة reset الخاصة به. ما الذي يجب أن تفعله؟ عادةً، يتم إعادة تعيين حقول الكائن ActionForm إلى القيم الافتراضية. لكن في هذه الحالة، لن نقوم بذلك، لأن الكائن dynaFormulaire يتم وضعه في الجلسة (scope="session"). لذلك يجب أن تحتفظ حقول dynaFormulaire بقيمها. ما هي هذه القيم عند الإنشاء الأولي للكائن dynaFormulaire؟ هناك حالتان:
- الحقل له قيمة أولية محددة في ملف التكوين:
في هذه الحالة، سيقوم وحدة التحكم Struts بإنشاء هذا الحقل بهذه القيمة الأولية.
- لا يحتوي الحقل على قيمة أولية حسب التكوين: تُطبق قواعد التهيئة في Java. بشكل عام، ستكون قيمة الحقول الرقمية صفرًا، وستكون قيمة سلاسل الأحرف هي السلسلة الفارغة، وستكون قيمة الكائنات الأخرى هي null.
لنلقِ نظرة على التكوين الأولي لـ dynaFormulaire:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
ستكون القيم الأولية لحقول dynaFormulaire بعد إنشائها كما يلي:
الحقل | القيمة الأولية |
"لا" | |
سلسلة فارغة | |
سلسلة فارغة | |
سلسلة فارغة | |
سلسلة فارغة | |
سلسلة فارغة | |
سلسلة فارغة | |
مصفوفة من السلاسل الفارغة | |
"xxx" | |
مصفوفة من السلاسل الفارغة |
- يمكننا أن نتصور أن طريقة reset الخاصة بـ dynaFormulaire تُعطي قيمًا للمصفوفات الثلاثة التي يجب أن تغذي القوائم الثلاث في العرض formulaire.jsp. وهذا ممكن هنا لأن بيانات هذه المصفوفات الثلاثة يتم إنشاؤها بشكل تعسفي. ومع ذلك، فإن الحالة الأكثر شيوعًا هي أن هذه البيانات تأتي من نموذج التطبيق، أي النموذج M الخاص بـ MVC. وهنا، سنتخذ موقفًا وسطًا، حتى لا نعقد المثال، وذلك بجعل الإجراء InitFormulaireAction هو الذي يولد هذه القيم، أي من خلال C في MVC.
- ليس هناك ضرورة لكتابة طريقة reset في dynaFormulaire، حيث إن الفئة ActionForm التي تنحدر منها تحتوي على طريقة من هذا النوع لا تقوم بأي شيء (لا توجد عمليات تهيئة).
- بمجرد استدعاء طريقة reset في dynaFormulaire، يتحقق المتحكم من السمة validate الخاصة بالإجراء. هنا، تكون قيمتها "false". ولن يتم استدعاء طريقة validate في dynaFormulaire.
- يتم إنشاء الكائن InitFormulaireAction أو إعادة استخدامه إذا كان موجودًا بالفعل، ثم يتم تشغيل طريقة execute الخاصة به. وهذه الطريقة هي التي ستقوم بتعيين قيم عشوائية للمصفوفات الثلاثة التابعة لـ dynaFormulaire: valeursCombo و valeursListeSimple و valeursListeMultiple. تُرجع الطريقة كائن ActionForward ذي المفتاح "afficherFormulaire".
- يقوم وحدة التحكم بعرض العرض /vues/formulaire.jsp الذي تم ربطه بالمفتاح "afficherFormulaire" بواسطة علامة إعادة توجيه من الإجراء /init.
الإجراء /confirmation
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- تتم عملية /confirmation عندما يضغط المستخدم على الزر [Envoyer] في العرض رقم 1. ثم يقوم المتصفح بـ"إرسال" النموذج الذي ملأه المستخدم إلى وحدة التحكم Struts.
- يتم إدراج الكائن dynaFormulaire في الجلسة
- ويتم استدعاء طريقة reset الخاصة به. وبمجرد استدعاء هذه الطريقة، يقوم وحدة التحكم Struts بنسخ قيم حقول النموذج الذي أرسله العميل إلى الحقول التي تحمل نفس الأسماء في dynaFormulaire. لنستعرض قائمة حقول هذا الأخير ونرى كيف تتم عملية النسخ هذه:
الحقل | رمز HTML المرتبط | قيمة الحقل بعد نسخ قيم النموذج |
<input type="radio" name="opt" value="نعم">نعم <input type="radio" name="opt" value="non" checked="checked">لا | - «نعم» أو «لا» حسب زر الاختيار المحدد | |
<input type="checkbox" name="chk1" value="on"> | - "on" إذا تم تحديد خانة chk1 - يحتفظ بقيمته السابقة إذا لم يتم تحديد المربع chk1 | |
<input type="checkbox" name="chk2" value="on"> | - "on" إذا تم تحديد المربع chk2 - يحتفظ بقيمته السابقة إذا لم يتم تحديد المربع chk2 | |
<input type="checkbox" name="chk2" value="on"> | - "on" إذا تم تحديد المربع chk3 - يحتفظ بقيمته السابقة إذا لم يتم تحديد المربع chk3 | |
<input type="text" name="champSaisie" value=""> | - القيمة التي أدخلها المستخدم في champSaisie | |
<input type="password" name="mdp" value=""> | - القيمة التي أدخلها المستخدم في mdp | |
<textarea name="boiteSaisie"></textarea> | - القيمة التي أدخلها المستخدم في boiteSaisie | |
<select name="combo">...</select> | - القيمة التي اختارها المستخدم في combo | |
<select name="listeSimple" size="3">...</select> | - القيمة التي اختارها المستخدم في listeSimple | |
<select name="listeMultiple" multiple="multiple" size="5"> | - مصفوفة من السلاسل تحتوي على القيم التي اختارها المستخدم في listeMultiple | |
<input type="hidden" name="secret" value="xxx"> | - "xxx". |
نواجه صعوبة مع الحقول التي لا تتلقى بالضرورة قيمة في الطلب المرسل من المتصفح. وهذا هو الحال بالنسبة لمربعات الاختيار من chk1 إلى chk3 والقائمتين listeSimple و listeMultiple. في هذه الحالة، تحتفظ هذه الحقول بقيمتها السابقة، أي تلك التي تم الحصول عليها خلال دورة الطلب والاستجابة السابقة.
لنأخذ مربع الاختيار chk1 على سبيل المثال، ولنفترض أنه في دورة الطلب والاستجابة السابقة، كان المستخدم قد حدد هذا المربع. عندئذ أرسل المتصفح في سلسلة معلمات طلبه المعلومات chk1="on". وبالتالي، قام المطور بتعيين القيمة "on" لحقل chk1 في dynaFormulaire. لنفترض الآن أنه في الدورة الحالية، لم يقم المستخدم بتحديد خانة chk1. في هذه الحالة، في سلسلة معلمات الطلب الجديد، لا يرسل المتصفح شيئًا مثل chk1="off" بل لا يرسل أي شيء على الإطلاق. ونتيجة لذلك، سيحتفظ الحقل chk1 في dynaFormulaire بقيمته "on"، وبالتالي سيكون له قيمة لا تعكس تلك الموجودة في النموذج الذي أقره المستخدم. سنستخدم طريقة reset في dynaFormulaire لحل هذه المشكلة. سنقوم في هذه الطريقة بتعيين الحقول الثلاثة chk1 و chk2 و chk3 على "off". في مثالنا الخاص بـ chk1، إما أن يقوم المستخدم:
- يضع علامة في خانة chk1، فإن المتصفح يرسل المعلومات chk1="on" وسيتحول الحقل chk1 في dynaFormulaire إلى "on"
- لم يضع علامة في خانة chk1. عندئذٍ لا يرسل المتصفح أي قيمة للحقل chk1 الذي سيحتفظ بقيمته السابقة "off". في كلتا الحالتين، تكون القيمة المسجلة في الحقل chk1 في dynaFormulaire صحيحة.
المشكلة مماثلة بالنسبة للقائمتين listeSimple و listeMultiple. إذا لم يتم تحديد أي خيار في هاتين القائمتين، فلن تظهر في معلمات الاستعلام، وبالتالي ستحتفظ بقيمها السابقة. في طريقة reset الخاصة بـ dynaFormulaire، سنقوم بإعادة تعيين listeSimple بقيمة فارغة، و listeMultiple بمصفوفة من السلاسل بطول صفر.
- بمجرد استدعاء طريقة reset الخاصة بـ dynaFormulaire، يقوم وحدة التحكم بنسخ المعلومات التي تم إرسالها إليه في طلب العميل إلى حقول dynaFormulaire
- يتم إنشاء كائن ForwardAction أو إعادة استخدامه، ويتم استدعاء طريقة execute الخاصة به. ForwardAction هي فئة محددة مسبقًا تُرجع كائن ActionForward يشير إلى العرض المحدد بواسطة السمة "parameter" للإجراء، وهو هنا /vues/confirmation.jsp.
- يقوم وحدة التحكم بإرسال هذه العرض. وبذلك يكتمل الدورة.
تقوم الإجراء /بعرض
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- يتم تشغيل الإجراء /affiche عن طريق تنشيط الرابط [Retour vers le formulaire] في العرض رقم 2.
- لا يوجد هنا نموذج مرتبط بالإجراء. لذلك ننتقل مباشرةً إلى تنفيذ الأسلوب execute لكائن ForwardAction الذي سيعيد كائن ActionForward يشير إلى العرض /vues/formulaire.jsp.
6.3.6. ملف رسائل التطبيق
القسم الثالث من الملف struts-config.xml هو ملف الرسائل:
يتم وضع الملف ApplicationResources.properties في WEB-INF/classes. وسيكون فارغًا. ورغم أنه فارغ، إلا أنه يجب الإعلان عنه في ملف التكوين، وإلا فإن مكتبة علامات struts-bean التي سنستعرضها لاحقًا ستولد خطأً. تُستخدم هذه المكتبة من قِبل العرض confirmation.jsp.
6.4. كود العروض
6.4.1. عرض formulaire.jsp
تجدر الإشارة إلى أن هذه العرضة تُعرض في حالتين:
- عند استدعاء الإجراء /init خلال الدورة الأولى من الطلب والاستجابة
- عند استدعاء الإجراء /affiche خلال الدورات التالية
كود العرض formulaire.jsp هو كما يلي:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>formulaire</title>
</head>
<body background='<html:rewrite page="/images/standard.jpg"/>'>
<h3>Formulaire Struts</h3>
<hr>
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<table border="0">
<tr>
<td>bouton radio</td>
<td>
<html:radio name="dynaFormulaire" property="opt" value="oui">Oui</html:radio>
<html:radio name="dynaFormulaire" property="opt" value="non">Non</html:radio>
</td>
</tr>
<tr>
<td>Cases à cocher</td>
<td>
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>
<html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox>
<html:checkbox name="dynaFormulaire" property="chk3">3</html:checkbox>
</td>
</tr>
<tr>
<td>Champ de saisie</td>
<td>
<html:text name="dynaFormulaire" property="champSaisie" />
</td>
</tr>
<tr>
<td>Mot de passe</td>
<td>
<html:password name="dynaFormulaire" property="mdp" />
</td>
</tr>
<tr>
<td>Boîte de saisie multilignes</td>
<td>
<html:textarea name="dynaFormulaire" property="boiteSaisie" />
</td>
</tr>
<tr>
<td>Combo</td>
<td>
<html:select name="dynaFormulaire" property="combo">
<html:options name="dynaFormulaire" property="valeursCombo"/>
</html:select>
</td>
</tr>
<tr>
<td>
<table>
<tr>
<td>Liste à sélection unique</td>
</tr>
<tr>
<td>
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
</td>
</tr>
</table>
<td>
<html:select name="dynaFormulaire" property="listeSimple" size="3">
<html:options name="dynaFormulaire" property="valeursListeSimple"/>
</html:select>
</td>
</tr>
<tr>
<td>
<table>
<tr>
<td>Liste à sélection multiple</td>
</tr>
<tr>
<td>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
</td>
</tr>
</table>
</td>
<td>
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
<html:options name="dynaFormulaire" property="valeursListeMultiple"/>
</html:select>
</td>
</tr>
</table>
<html:hidden name="dynaFormulaire" property="secret"/>
<br>
<hr>
<html:submit>Envoyer</html:submit>
</html:form>
</body>
</html>
تستخدم هذه الصفحة JSP علامات مستمدة من مكتبة struts-html. تجدر الإشارة إلى أنه لاستخدام مكتبة العلامات، يجب:
- إعلانها في ملف web.xml الخاص بالتطبيق باستخدام علامة <tag-lib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
- ضع كود هذه المكتبة في مكان ما ضمن شجرة التطبيق، هنا WEB-INF/struts-html.tld
- أعلن استخدام هذه المكتبة في بداية الصفحات JSP التي تستخدمها:
تستخدم طريقة العرض formulaire.jsp علامات سنشرحها الآن:
<body background="<html:rewrite page="/images/standard.jpg"/>"> | |||
تسمح علامة html:rewrite بالتخلص من اسم التطبيق في URL. ولها سمة واحدة:
وبالتالي، كما هو موضح أعلاه، إذا قررنا تسمية التطبيق بـ «formulaire3»، فلن يكون هناك داعٍ لإعادة كتابة كود السمة background. ستقوم العلامة html:rewrite بإنشاء الكود الجديد HTML background="/formulaire3/images/standard.jpg" |
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire"> | |||||||
تسمح علامة html:form بإنشاء نموذج HTML. ولها عدة سمات:
نلاحظ أنه بشكل افتراضي، يستخدم الكود HTML الذي تم إنشاؤه الطريقة POST. في نفس الرمز HTML، تمت إعادة كتابة الرمز URL الخاص بالإجراء بحيث يسبقه اسم التطبيق وتليها اللاحقة .do. |
<html:radio name="dynaFormulaire" property="opt" value="نعم">نعم</html:radio> | |||||||
تُستخدم علامة html:radio لإنشاء العلامة HTML <input type="radio" ...>. وهي تدعم العديد من السمات:
النص الموجود بين علامتي البداية والنهاية هو النص الذي سيُعرض بجانب زر الاختيار. |
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox> | |||||||
تُستخدم علامة html:checkbox لإنشاء العلامة HTML <input type="checkbox" ...>. وهي تدعم العديد من السمات:
النص الموجود بين علامتي البداية والنهاية هو النص الذي سيُعرض بجانب مربع الاختيار. |
<html:text name="dynaFormulaire" property="champSaisie" /> | |||||||
تُستخدم علامة html:text لإنشاء العلامة HTML <input type="text" ...>. وهي تدعم العديد من السمات:
|
<html:password name="dynaFormulaire" property="mdp" /> | |
تُستخدم علامة html:password لإنشاء العلامة HTML <input type="password" ...>. وهي تدعم العديد من السمات: |
<html:textarea name="dynaFormulaire" property="boiteSaisie" /> | |||||||
تُستخدم علامة html:textarea لإنشاء العلامة HTML <textarea>...</textarea>. وهي تدعم العديد من السمات:
|
<html:select name="dynaFormulaire" property="combo">....</html:select> | |||||||
تُستخدم علامة html:select لإنشاء العلامة HTML <select>...</select>. وهي تدعم العديد من السمات:
|
<html:select name="dynaFormulaire" property="combo"> <html:options name="dynaFormulaire" property="valeursCombo"/> </html:select> | |||||
تُستخدم علامة html:options لإنشاء العلامات HTML <option>...</option> داخل علامة HTML <select>. هناك طرق متنوعة لتحديد كيفية العثور على قيم ملء قائمة الاختيار. هنا، استخدمنا السمتين name و property:
|
يتم إنشاء القائمتين الأخريين بطريقة مشابهة للقائمة السابقة:
<html:select name="dynaFormulaire" property="listeSimple" size="3">
<html:options name="dynaFormulaire" property="valeursListeSimple"/>
</html:select>
في المثال أعلاه، تم تحديد السمة size بقيمة مختلفة عن 1 للحصول على قائمة بدلاً من قائمة منسدلة.
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
<html:options name="dynaFormulaire" property="valeursListeMultiple"/>
</html:select>
فيما يلي، يتم تحديد السمة multiple="true" للحصول على قائمة تسمح بالاختيار المتعدد.
<html:hidden name="dynaFormulaire" property="secret"/> | |||||
تُستخدم علامة html:hidden لإنشاء العلامة HTML <input type="hidden" ...>.
|
لفهم العلاقة بين العرض formulaire.jsp و«بيان» dynaFormulaire الذي يمثله في الذاكرة، يجب تذكر أن «بيان» dynaFormulaire يُستخدم في كل من القراءة والكتابة:
![]() |
يتم تنفيذ الطلب عندما ينقر المستخدم على الزر [Envoyer] في النموذج. عندئذٍ يقوم المتصفح بـ«إرسال» النموذج HTML إلى الإجراء /confirmation. لقد أوضحنا سابقًا ما يحدث عندئذٍ، ولا سيما أن حقول dynaFormulaire ستتلقى قيم الحقول التي تحمل نفس الاسم في النموذج HTML.
ماذا يحدث عندما يطلب وحدة التحكم عرض العرض formulaire.jsp استجابةً لطلب ما؟ لنراجع العلامات واحدةً تلو الأخرى:
<body background="<html:rewrite page="/images/standard.jpg"/>"> | |
يولد الكود HTML |
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire"> ... </html:form> | |
يُنتج الرمز HTML |
<html:radio name="dynaFormulaire" property="opt" value="نعم">نعم</html:radio> <html:radio name="dynaFormulaire" property="opt" value="لا">لا</html:radio> | |
إذا كانت قيمة الحقل opt في dynaFormulaire هي "نعم"، فقم بإنشاء الرمز HTML |
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox> <html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox> <html:checkbox name="dynaFormulaire" property="chk3">3</html:checkbox> | |
إذا كانت قيمتي الحقلين chk1 و chk3 في dynaFormulaire هما "on" وكانت قيمة الحقل chk2 هي "off"، فقم بإنشاء الرمز HTML |
<html:text name="dynaFormulaire" property="champSaisie" /> | |
إذا كانت قيمة الحقل champSaisie هي "هذا اختبار"، فسيتم إنشاء الرمز HTML |
<html:password name="dynaFormulaire" property="mdp" /> | |
إذا كانت قيمة الحقل mdp هي "azerty"، يتم إنشاء الرمز HTML |
<html:password name="dynaFormulaire" property="mdp" /> | |
إذا كانت قيمة الحقل mdp هي "azerty"، يتم إنشاء الرمز HTML |
<html:password name="dynaFormulaire" property="mdp" /> | |
إذا كانت قيمة الحقل mdp هي "azerty"، يتم إنشاء الرمز HTML |
<html:select name="dynaFormulaire" property="combo"> <html:options name="dynaFormulaire" property="valeursCombo"/> </html:select> | |
إذا كانت قيمة الحقل combo هي "combo2"، فسيتم إنشاء الرمز HTML |
<html:select name="dynaFormulaire" property="listeSimple" size="3"> <html:options name="dynaFormulaire" property="valeursListeSimple"/> </html:select> | |
إذا كانت قيمة الحقل listeSimple هي "simple1"، فسيتم إنشاء الرمز HTML |
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true"> <html:options name="dynaFormulaire" property="valeursListeMultiple"/> </html:select> | |
إذا كان الحقل listeMultiple عبارة عن المصفوفة {"multiple0","multiple2"}، فيتم إنشاء الرمز HTML |
<html:hidden name="dynaFormulaire" property="secret"/> | |
إذا كانت قيمة الحقل السري هي "xxx"، فقم بإنشاء الرمز HTML |
<html:submit>إرسال</html:submit> | |
يُنتج الرمز HTML |
آخر ما يجب شرحه هو كود جافا سكريبت المضمن في الصفحة JSP والمرتبط بالزرين [Effacer] اللذين يلغيان تحديد العناصر المحددة في القائمتين listeSimple و listeMultiple:
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
العلامة
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
ينتج عنه الرمز HTML التالي:
لفهم كود جافا سكريبت المرتبط بالأزرار [Effacer]، دعونا نستعرض كيفية تسمية العناصر المختلفة في مستند ويب ضمن كود جافا سكريبت يستخدم هذا المستند:
البيانات | المعنى |
تشير إلى المستند الويب بأكمله | |
يشير إلى مجموعة النماذج المحددة في المستند | |
يشير إلى النموذج رقم i في المستند | |
يشير إلى النموذج <form> الذي يساوي سمة name فيه "nomFormulaire" | |
يشير إلى النموذج <form> الذي يكون سمة name فيه "nomFormulaire" | |
يشير إلى مجموعة العناصر المكونة للنموذج المشار إليه بالتعبير [formulaire]. تتضمن هذه المجموعة جميع العلامات <input> و<textarea> و<select> الخاصة بالنموذج المشار إليه. | |
يشير إلى العنصر رقم i من [formulaire] | |
يشير إلى العنصر في [formulaire] الذي يساوي السمة «name» له nomComposant | |
يشير إلى العنصر في [formulaire] الذي يكون فيه السمة name مساوية لـ nomComposant | |
يشير إلى قيمة المكون [composant] في النموذج [formulaire] عندما يمكن أن يكون للرمز HTML الخاص به سمة value (<input>, <textarea>) | |
يشير إلى مؤشر الخيار المحدد في قائمة. يُستخدم في القراءة والكتابة. يؤدي تعيين هذه الخاصية إلى -1 إلى إلغاء تحديد جميع عناصر القائمة. | |
تشير إلى مصفوفة الخيارات المرتبطة بعلامة <select> | |
يشير إلى الخيار رقم i لعلامة <select> المحددة | |
قيمة منطقية تشير إلى ما إذا كان الخيار رقم i لعلامة [select] المشار إليها محددًا (true) أم لا. يمكن استخدامها للقراءة والكتابة |
لنعد إلى كود جافا سكريبت الخاص بالزرين:
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
عند النقر على الزر، يتم تنفيذ الكود المرتبط بالسمة "onclick". هنا، الكود مكتوب في سطر واحد. غالبًا ما نكتب onclick="دالة(...)" حيث الدالة هي دالة محددة داخل علامة <script language="javascript">...</script>. ما الذي يفعله الكود أعلاه؟ لنعلق على كود الزر الأول:
يشير إلى مستند الويب الذي يوجد فيه الزر | |
يشير إلى النموذج الذي يوجد فيه الزر | |
يشير إلى مكون listeSimple في النموذج | |
يشير إلى مؤشر الخيار المحدد في listeSimple. يؤدي تعيين هذه الخاصية إلى -1 إلى إلغاء تحديد جميع الخيارات. |
6.4.2. الطريقة confirmation.jsp
تجدر الإشارة إلى أن هذه العرضة تُعرض عند انتهاء الإجراء /confirmation، c.a.d، بعد أن يقوم عميل الويب بإرسال النموذج الموجود في العرضة formulaire.jsp. والغرض الوحيد منها هو عرض القيم التي أدخلها المستخدم. وفيما يلي كودها:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html>
<head>
<title>Confirmation</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h3>Confirmation des valeurs saisies</h3>
<hr/>
<table border="1">
<tr>
<td>Bouton radio</td>
<td><bean:write name="dynaFormulaire" scope="session" property="opt"/></td>
</tr>
<tr>
<td>Case à cocher chk1</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk1"/></td>
</tr>
<tr>
<td>Case à cocher chk2</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk2"/></td>
</tr>
<tr>
<td>Case à cocher chk3</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk3"/></td>
</tr>
<tr>
<td>Champ de saisie</td>
<td><bean:write name="dynaFormulaire" scope="session" property="champSaisie"/></td>
</tr>
<tr>
<td>Mot de passe</td>
<td><bean:write name="dynaFormulaire" scope="session" property="mdp"/></td>
</tr>
<tr>
<td>Boîte de saisie</td>
<td><bean:write name="dynaFormulaire" scope="session" property="boiteSaisie"/></td>
</tr>
<tr>
<td>combo</td>
<td><bean:write name="dynaFormulaire" scope="session" property="combo"/></td>
</tr>
<tr>
<td>liste simple</td>
<td><bean:write name="dynaFormulaire" scope="session" property="listeSimple"/></td>
</tr>
<logic:iterate id="choix" indexId="index" name="dynaFormulaire" property="listeMultiple">
<tr>
<td>liste multiple[<bean:write name="index"/>]</td>
<td><bean:write name="choix"/></td>
</tr>
</logic:iterate>
</table>
<br>
<html:link page="/affiche.do">
Retour au formulaire
</html:link>
</body>
</html>
نقدم هنا مكتبتين جديدتين لعلامات الترميز: struts-bean و struts-logic. تتيح مكتبة struts-bean الوصول إلى الكائنات الموجودة في الطلب أو الجلسة أو سياق التطبيق. أما مكتبة struts-logic فتتيح إدخال منطق التنفيذ باستخدام العلامات. وهاتان المكتبتان ليستا ضروريتين على الإطلاق. وكما رأينا، يمكن لصفحة JSP:
- استرداد كائنات من الطلب (request.getAttribute(...))، أو الجلسة (session.getAttribute(...))، أو سياق التطبيق
- تضمين أجزاء ديناميكية في الكود HTML باستخدام المتغيرات <%= متغير %>
- تضمين كود Java <% كود Java %>
إن تضمين كود Java في صفحات JSP يزعج كل من يرغب في فصل صارم بين منطق التطبيق (كود Java) والعرض (استخدام العلامات). ولذلك تم ابتكار مكتبات العلامات خصيصًا لهم.
سنتبع نفس الإجراء المتبع في العرض formulaire.jsp ونشرح كل علامة موجودة في كود confirmation.jsp إذا لم نكن قد صادفناها من قبل في العرض formulaire.jsp. أولاً، نلاحظ أن الصفحة تبدأ بالإعلان عن مكتبات العلامات الثلاث التي ستستخدمها:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
ولنتذكر أيضًا أن هذه المكتبات الثلاث يجب الإعلان عنها في ملف web.xml الخاص بالتطبيق. سنقوم الآن بتعليق العلامات في المستند formulaire.jsp:
تقوم بكتابة قيمة في التدفق HTML الحالي. تقبل العلامة bean:write السمات التالية: name: اسم الكائن المراد استخدامه scope: النطاق (request، session، context) الذي يجب البحث فيه عن هذا الكائن property: حقل الكائن المحدد بواسطة name الذي يجب كتابة خاصيته. يمكن أن يكون هذا الحقل كائنًا من أي نوع. وسيتم استخدام الطريقة toString الخاصة بالكائن. هنا، يتم كتابة قيمة الحقل opt الخاص بـ dynaFormulaire. وستكون القيمة «نعم» إذا قام المستخدم بتحديد زر الاختيار الذي يحمل السمة value="نعم"، أو «لا» إذا قام بتحديد زر الاختيار الذي يحمل السمة value="لا" |
يكتب قيمة الحقل chk1 من dynaFormulaire. ستكون القيمة «on» إذا قام المستخدم بتحديد المربع، أو «off» في حالة عدم تحديده. وينطبق الأمر نفسه على chk2 و chk3. |
يكتب قيمة الحقل champSaisie من dynaFormulaire، أي النص الذي أدخله المستخدم في هذا الحقل. وينطبق الأمر نفسه على mdp و boiteSaisie. |
يكتب قيمة الحقل المنسدل لـ dynaFormulaire. سنحصل على السمة value للعنصر <option> الذي اختاره المستخدم. |
يكتب قيمة الحقل listeSimple من dynaFormulaire. سيتم الحصول على السمة value للعنصر <option> الذي اختاره المستخدم، إن وجد. وإلا فسيتم الحصول على سلسلة فارغة. |
هنا، نستخدم علامات منطقية. نحن نتعامل مع قائمة متعددة الخيارات. قيمة الحقل listeMultiple الخاص بالكائن dynaFormulaire هي مصفوفة من سلاسل النص (String). في لغة جافا، كنا سنكتب حلقة تكرار. تسمح لنا علامة logic:iterate بتنفيذ هذه الحلقة نفسها دون كتابة كود جافا. تحتوي علامة logic:iterate في المثال على السمات التالية: name="dynaFormulaire": اسم الكائن المراد استخدامه property="listeMultiple": اسم الخاصية التي تحتوي، في الكائن المشار إليه بواسطة name، على المجموعة التي سيتم تصفحها في الحلقة. وهنا، هذه المجموعة هي مصفوفة القيم المحددة في listeMultiple. وقد تكون هذه المصفوفة فارغة. id="choix": معرّف يشير إلى العنصر الحالي في المصفوفة في كل دورة من دورات الحلقة. في الدورة الأولى، سيمثل «choix» listeMultiple[0]، وفي الدورة الثانية سيمثل listeMultiple[1]، وهكذا دواليك. indexID="index": معرّف يشير إلى مؤشر العنصر الحالي في المصفوفة في كل دورة من دورات الحلقة. في الدورة الأولى، ستكون قيمة index هي 0، وفي الدورة الثانية ستكون القيمة 1، وهكذا دواليك. يتم تكرار الكود HTML الموجود بين العلامتين <logic:iterate ...> و</logic:iterate> لكل عنصر في المجموعة المشار إليها بالزوج (name,property). الجزء الديناميكي من هذا الكود هو التالي:
بناءً على ما سبق، عند التكرار رقم i (i>=0)، يكون الكود HTML الذي تم إنشاؤه مكافئًا للكود التالي: |
يُنشئ رابطًا مرتبطًا بسياق التطبيق، مما يغني عن الحاجة إلى معرفة هذا السياق. الرمز HTML الذي تم إنشاؤه بواسطة هذه العلامة هو التالي: |
6.5. فئات Java
يشير ملف التكوين struts-config.xml إلى فئتين في Java:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
الفئة DynaFormulaire هي الفئة التي ستحتوي على قيم العرض رقم 1 formulaire.jsp. الفئة InitFormulaireAction هي الفئة التي ستعالج قيم النموذج الذي تم إرساله عبر الزر [Envoyer] التابع لـ formulaire.jsp.
6.5.1. الفئة DynaFormulaire
لاستيعاب قيم النموذج، يكفي استخدام كائن من النوع DynaActionForm ما لم تكن هناك حاجة إلى إعادة تعريف إحدى الطريقتين reset أو validate لهذه الفئة. هنا، لا تحتاج الطريقة validate إلى إعادة تعريف لأننا لا نقوم بأي عملية تحقق من صحة البيانات. لكن الأسلوب reset يحتاج إلى إعادة تعريف. في الواقع، ستتلقى حقول الكائن DynaFormulaire قيمها من النموذج الذي أرسله عميل الويب. ومع ذلك، قد لا تتلقى بعض الحقول أي قيمة إذا لم تكن موجودة في الطلب. ويحدث هذا في الحالات التالية:
- مربع اختيار لم يقم المستخدم بتحديده
- قائمة تحتوي على أكثر من خيار واحد أو لم يتم تحديد أي خيار
بالنسبة للنماذج التي تحتوي على هذا النوع من المكونات، يجب أن تقوم طريقة reset
- تعيين القيمة "off" للحقل المرتبط بمربع الاختيار
- تعيين سلسلة فارغة للحقل المرتبط بقائمة الاختيار الفردي
- تعيين مصفوفة ذات حجم صفر من سلاسل الأحرف للحقل المرتبط بقائمة متعددة الخيارات
وبالتالي، إذا لم تتلقى هذه الحقول أي قيمة من الاستعلام، فإنها تحتفظ بالقيمة التي تم تعيينها بواسطة reset، وهي القيمة التي تتوافق مع حالة المكون في النموذج الذي تم التحقق من صحته من قبل المستخدم (مربع غير محدد، قائمة بدون أي عنصر محدد).
فيما يلي كود الفئة DynaFormulaire، وهي فئة مشتقة من DynaActionForm:
package istia.st.struts.formulaire;
import org.apache.struts.action.DynaActionForm;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
public class DynaFormulaire extends DynaActionForm {
public void reset(ActionMapping mapping, HttpServletRequest request){
// إعادة تعيين مربعات الاختيار - القيمة «off»
set("chk1","off");
set("chk2","off");
set("chk2","off");
// إعادة تعيين listeSimple - سلسلة فارغة
set("listeSimple","");
// إعادة تعيين listeMultiple - مصفوفة فارغة
set("listeMultiple",new String[]{});
}
}
6.5.2. الفئة InitFormulaireAction
الفئة InitFormulaireAction مرتبطة، في الملف struts-config.xml، بالإجراء /init:
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
يتم استخدام الإجراء /init مرة واحدة فقط أثناء الإنشاء الأولي للكائن DynaFormulaire. والغرض منه هو توفير محتوى للقوائم الثلاث في النموذج المنسدل، listeSimple، listeMultiple. يتم توفير هذا المحتوى في شكل ثلاثة جداول، وهي خصائص الكائن dynaFormulaire:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
...
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
بمجرد تهيئة الجداول valeursCombo و valeursListeSimple و valeursListeMultiple بواسطة InitFormulaireAction، لن تكون هناك حاجة إلى تهيئتها مرة أخرى. فالكائن dynaFormulaire يتم وضعه في الجلسة، وبالتالي يحتفظ بقيمته عبر دورات الطلب والاستجابة. ولهذا السبب لا يتم تنفيذ الإجراء /init إلا مرة واحدة. وفيما يلي كود InitFormulaireAction:
package istia.st.struts.formulaire;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class InitFormulaireAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
// تحضير النموذج للعرض
// يتم إدخال المعلومات اللازمة للنموذج في bean الخاص به
DynaFormulaire formulaire = (DynaFormulaire) form;
formulaire.set("valeursCombo", getValeurs(5, "combo"));
formulaire.set("valeursListeSimple", getValeurs(7, "simple"));
formulaire.set("valeursListeMultiple", getValeurs(10, "multiple"));
// إرجاع السيطرة
return mapping.findForward("afficherFormulaire");
} //يتم التنفيذ
// قائمة قيم قائمة الاختيار
private String[] getValeurs(int taille, String label) {
String[] valeurs = new String[taille];
for (int i = 0; i < taille; i++) {
valeurs[i] = label + i;
}
return valeurs;
}
}
- تُشتق الفئة من فئة Action. وهذا أمر إلزامي.
- يستخدم وحدة التحكم Struts كائن Action عبر طريقة execute الخاصة به. لذا، يجب إعادة تعريف هذه الطريقة. تتلقى هذه الطريقة المعلمات التالية:
- ActionMapping mapping: كائن يمثل تكوين التطبيق في struts-config.xml
- ActionForm form: النموذج المرتبط بالإجراء، إذا كان هناك نموذج محدد في تكوين الإجراء (السمة name للإجراء).
- HttpServletRequest request: طلب العميل
- HttpServletResponse: الرد المرسَل إلى العميل
- يجب أن تقوم الفئة InitFormulaireAction بتهيئة النموذج dynaFormulaire. ويصل هذا النموذج إلى الأسلوب execute في شكل المعلمة ActionForm form. تجدر الإشارة إلى أن dynaFormulaire هي من نوع DynaFormulaire، وهي فئة مشتقة من الفئة DynaActionForm التي هي بدورها مشتقة من الفئة ActionForm.
- في الطريقة execute، يتم تعيين قيم للحقول الثلاثة valeursCombo و valeursListeSimple و valeursListeMultiple باستخدام الطريقة set الخاصة بالفئة DynaActionForm. هذه القيم عبارة عن مصفوفات عشوائية تهدف إلى التبسيط. تجدر الإشارة إلى أن الطريقة set تُعيّن قيمة لحقل موجود. ولا يمكن استخدامها لإنشاء حقول جديدة. ولهذا السبب، من الضروري تعريف الحقول الثلاثة valeursCombo و valeursListeSimple و valeursListeMultiple في تعريف الكائن dynaFormulaire في struts-config.xml.
- تنتهي طريقة execute بإرجاع مفتاح العرض المراد عرضه إلى وحدة التحكم كاستجابة للعميل. هنا، المفتاح afficherFormulaire هو الذي تم ربطه في الملف struts-config.xml بعرض /vues/formulaire.jsp.
6.6. النشر
هيكل التطبيق كالتالي:
![]() | ![]() |
![]() | ![]() |


تجدر الإشارة إلى أن الملف ApplicationResources.properties المذكور أعلاه ضروري لمكتبة العلامات struts-bean. ومن المعروف أن هذا الملف يحتوي على رسائل التطبيق. ويمكن لمكتبة struts-bean الوصول إلى هذه الرسائل. هنا، لا يحدد تطبيقنا أي رسالة. كما أن الملف ApplicationResources.properties موجود ولكنه فارغ.
6.7. الخلاصة
لقد تناولنا بالتفصيل في هذا الدرس كيفية إدارة المكونات المختلفة لنموذج HTML. يمكننا الآن استخدام نماذج معقدة في تطبيقات Struts الخاصة بنا.






