4. استخدام النماذج الديناميكية
4.1. إعلان النموذج الديناميكي
لقد رأينا أن Struts تستخدم كائنات ActionForm لتخزين قيم النماذج HTML التي تعالجها سيرفلتات التطبيق المختلفة. لكل نموذج في تطبيقنا، يتعين علينا إنشاء فئة مشتقة من ActionForm. وقد يصبح هذا الأمر مرهقًا بسرعة، حيث يتعين علينا كتابة طريقتين، هما setXx و getXx، لكل حقل xx. يوفر لنا Struts إمكانية استخدام نماذج
- يتم تعريف هيكلها في الملف struts-config.xml ضمن القسم <form-beans>
- والتي يتم إنشاؤها ديناميكيًا بواسطة بيئة Struts وفقًا للبنية المُعلنة
وبالتالي، يمكن تعريف الفئة المستخدمة لتخزين قيم الاسم والعمر في تطبيق strutspersonne على النحو التالي:
<form-beans>
<form-bean name="frmPersonne" type="org.apache.struts.actions.DynaActionForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
لكل حقل في النموذج، يتم تعريف علامة <form-property> بسمتين:
- name: اسم الحقل
- type: نوعه في لغة جافا
نظرًا لأن قيم النموذج التي يرسلها عميل الويب هي سلاسل أحرف، فإن الأنواع الأكثر استخدامًا هي java.lang.String للحقول ذات القيمة الواحدة وjava.lang.String[] للحقول ذات القيم المتعددة (مربعات الاختيار التي تحمل الاسم نفسه، وقوائم الاختيار المتعدد، ...). تحتوي الفئة DynactionForm، شأنها شأن الفئة ActionForm، على طريقة validate لا تؤدي أي وظيفة. ولجعلها تتحقق من صحة معلمات النموذج، يجب إنشاء فئة مشتقة منها وكتابة طريقة validate بنفسك. وبالتالي، سيكون إعلان النموذج كما يلي:
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
يجب أن نقوم بكتابة الفئة istia.st.struts.personne.PersonneDynaForm.
4.2. كتابة الفئة DynaActionForm المرتبطة بالنموذج الديناميكي
فيما سبق، قمنا بربط الفئة istia.st.struts.personne.PersonneDynaForm بالنموذج (الاسم، العمر). ونقوم الآن بكتابة هذه الفئة:
package istia.st.struts.personne;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class PersonneDynaForm extends DynaActionForm {
// التحقق من صحة البيانات
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
// معالجة الأخطاء
ActionErrors erreurs = new ActionErrors();
// يجب ألا يكون حقل الاسم فارغًا
String nom = (String)this.get("nom");
if (nom == null || nom.trim().equals("")) {
erreurs.add("nomvide", new ActionError("personne.formulaire.nom.vide"));
}
// يجب ألا يكون حقل العمر فارغًا
String age = (String)this.get("age");
if (age == null || age.trim().equals("")) {
erreurs.add("agevide", new ActionError("personne.formulaire.age.vide"));
}
else {
// يجب أن يكون العمر عددًا صحيحًا موجبًا
if (!age.matches("^\\s*\\d+\\s*$")) {
erreurs.add("ageincorrect", new ActionError("personne.formulaire.age.incorrect", age));
// عرض قائمة الأخطاء
}
} //if
// يتم عرض قائمة الأخطاء
return erreurs;
}
}//فئة
النقاط التي يجب ملاحظتها هي التالية:
- تُشتق الفئة من DynaActionForm
- تمت إعادة كتابة الطريقة validate الخاصة بالفئة DynaActionForm. عند تنفيذها بواسطة وحدة التحكم Struts، تم إنشاء الكائن PersonneDynaForm. وهو يحتوي على قاموس تحتوي مفاتيحه على حقول النموذج «الاسم» و«العمر»، وقيمه على قيم هذه الحقول. للوصول إلى حقل ما داخل طرق DynaActionForm، يتم استخدام الطريقة Object get(String nomDuChamp). وإذا أردنا تعيين قيمة لحقل ما، فيمكننا استخدام الطريقة void set(String nomDuChamp, Object valeur). يرجى الرجوع إلى تعريف الفئة DynaActionForm للحصول على التعريف الكامل.
- بمجرد استرداد قيم الحقلين «الاسم» و«العمر» من النموذج، لا تختلف طريقة validate عن تلك التي تمت كتابتها عندما كان النموذج مرتبطًا بكائن ActionForm.
4.3. الفئة الجديدة FormulaireAction
يحدد ملف التكوين الإجراء /main التالي:
...
<form-beans>
<form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
....
<action
path="/main"
name="frmPersonne"
scope="session"
validate="true"
input="/erreurs.do"
type="istia.st.struts.personne.FormulaireAction"
>
<forward name="reponse" path="/reponse.do"/>
</action>
هذا التعريف هو نفسه الموجود في تطبيق strutspersonne. يتم تنفيذ الإجراء /main بواسطة كائن من النوع FormulaireAction. كان هذا الكائن يتلقى قيم النموذج frmPersonne في كائن من النوع FormulaireBean. أما الآن، فيتلقى هذه القيم في كائن من النوع PersonneDynaForm. لذا، يجب إعادة كتابة الفئة:
package istia.st.struts.personne;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.ServletException;
import istia.st.struts.personne.PersonneDynaForm;
public class FormulaireAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// لدينا نموذج صالح، وإلا لما وصلنا إلى هذه المرحلة
PersonneDynaForm formulaire=(PersonneDynaForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//تنفيذ
}
النقاط التي يجب ملاحظتها هي التالية:
- يجب استيراد الفئة PersonneDynaForm التي تحتوي على بيانات النموذج للوصول إلى تعريفها
- تسترد الطريقة execute قيم معلمات النموذج باستخدام الطريقة [DynaActionForm].get.
بالنسبة للفئة FormulaireAction في تطبيق strutspersonne، لم يتغير سوى طريقة الوصول إلى قيم النموذج.
4.4. نشر واختبار تطبيق strutspersonne1
4.4.1. إنشاء السياق
أطلقنا على هذا التطبيق الجديد اسم strutspersonne1. نقوم بإنشاء تعريف جديد في ملف <tomcat>\conf\serveur.xml الخاص بـ Tomcat 4.x:
بعد الانتهاء من ذلك، يجب إعادة تشغيل Tomcat. يمكن التحقق من صحة السياق عن طريق طلب URL:
http://localhost:8080/strutspersonne1/

4.4.2. الطرق
سنقوم بنسخ مجلد «vues» الخاص بتطبيق strutspersonne إلى مجلد تطبيق strutspersonne1. ففي الواقع، لم تتغير الصفحات.

4.4.3. تجميع الفئات
لدينا فئتان يجب إنشاؤهما: PersonneDynaForm و FormulaireAction، حيث تستخدم الثانية الأولى. يمكننا إنشاؤهما وتجميعهما باستخدام مشروع JBuilder:

4.4.4. المجلد WEB-INF
سنقوم بنسخ المجلد WEB-INF من تطبيق strutspersonne إلى مجلد تطبيق strutspersonne1. ستتغير بعض الملفات:

يصبح ملف التكوين 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="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
<form-property name="nom" type="java.lang.String" initial=""/>
<form-property name="age" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/main"
name="frmPersonne"
scope="session"
validate="true"
input="/erreurs.do"
type="istia.st.struts.personne.FormulaireAction"
>
<forward name="reponse" path="/reponse.do"/>
</action>
<action
path="/erreurs"
parameter="/vues/erreurs.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/reponse"
parameter="/vues/reponse.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/formulaire"
parameter="/vues/formulaire.personne.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources parameter="ressources.personneressources"/>
</struts-config>
هذا الملف مطابق لملف تطبيق strutspersonne باستثناء التعريف الديناميكي للنموذج (الجزء المحاط بإطار).
في المجلد WEB-INF/classes، سنضع الفئات التي تم تجميعها بواسطة JBuilder:

في المجلد WEB-INF\classes\ressources، نضع ملف الرسائل. لم يتغير هذا الملف.
personne.formulaire.nom.vide=<li>Vous devez indiquer un nom</li>
personne.formulaire.age.vide=<li>Vous devez indiquer un age</li>
personne.formulaire.age.incorrect=<li>L'âge [{0}] est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

4.5. الاختبارات
نحن جاهزون لإجراء الاختبارات. فيما يلي بعض لقطات الشاشة التي يُطلب من القارئ محاكاتها.
نطلب من القارئ إعادة إنتاج URL http://localhost:8080/strutspersonne1/formulaire.do:

نستخدم الزر [Envoyer] دون ملء الحقول:

نكرر المحاولة مع وجود خطأ في حقل «العمر»:

نحصل على الرد التالي:

نحاول مرة أخرى، هذه المرة بإدخال القيم الصحيحة:

نحصل على الإجابة التالية:

4.6. الخلاصة
يسهل استخدام النماذج الديناميكية كتابة الفئات من النوع ActionForm المسؤولة عن تخزين قيم النماذج. ويمكننا الذهاب أبعد من ذلك قليلاً في تبسيط إدارة النماذج. في تطبيق strutspersonne1، أنشأنا فئة PersonneDynaForm للتحقق من صحة قيم (الاسم، العمر) في النموذج. في الممارسة العملية، تتكرر بعض عمليات التحقق بشكل متكرر: الحقل غير فارغ، الحقل الذي يتحقق من تعبير منتظم معين، الحقل الصحيح، حقل التاريخ، ... يمكن عندئذٍ طلب هذا النوع من عمليات التحقق القياسية في ملف التكوين struts-config.html. إذا كانت جميع عمليات التحقق المطلوبة «قياسية»، فلن يكون هناك حاجة لكتابة فئة خاصة بالنموذج. وهذا ما سنراه الآن.