14. التطبيق [SimuPaie] – الإصدار 10 – عميل Flex لخدمة ويب ASP.NET
نقدم الآن عميل Flex لخدمة الويب ASP.NET من الإصدار 5. IDE المستخدم هو Flex Builder 3. يمكن تنزيل نسخة تجريبية من هذا المنتج من URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. Flex Builder 3 هو IDE Eclipse. بالإضافة إلى ذلك، لتشغيل عميل Flex، نستخدم خادم ويب Apache من أداة Wamp. أي خادم Apache يفي بالغرض. يجب أن يتوفر في المتصفح الذي يعرض عميل Flex المكون الإضافي Flash Player الإصدار 9 كحد أدنى.
تتميز تطبيقات Flex بأنها تعمل داخل المكون الإضافي Flash Player للمتصفح. وفي هذا تشبه تطبيقات Ajax التي تضم في الصفحات المرسلة إلى المتصفح نصوص برمجية JavaScript يتم تنفيذها بعد ذلك داخل المتصفح. تطبيق Flex ليس تطبيق ويب بالمعنى المعتاد: إنه تطبيق عميل لخدمات تقدمها خوادم الويب. وفي هذا، فهو مشابه لتطبيق سطح مكتب يكون عميلاً لهذه الخدمات نفسها. لكنه يختلف في نقطة واحدة: يتم تنزيله مبدئيًا من خادم ويب داخل متصفح مزود بمكوّن Flash Player الإضافي القادر على تشغيله.
مثل تطبيق سطح المكتب، يتكون تطبيق Flex بشكل أساسي من عنصرين:
- جزء العرض: العروض المعروضة في المتصفح. تتمتع هذه العروض بثراء نوافذ تطبيقات سطح المكتب. يتم وصف العرض باستخدام لغة علامات تسمى MXML.
- جزء الكود الذي يدير بشكل أساسي الأحداث التي تسببها إجراءات المستخدم على العرض. يمكن كتابة هذا الكود أيضًا بلغة MXML أو بلغة موجهة للكائنات تسمى ActionScript. يجب التمييز بين نوعين من الأحداث:
- الحدث الذي يتطلب تبادلًا مع خادم الويب: ملء قائمة بالبيانات المقدمة من تطبيق ويب، وإرسال بيانات نموذج إلى الخادم، ... يوفر Flex عددًا من الطرق للتواصل مع الخادم بطريقة شفافة للمطور. هذه الطرق غير متزامنة بشكل افتراضي: يمكن للمستخدم الاستمرار في التفاعل مع العرض أثناء الطلب المرسَل إلى الخادم.
- الحدث الذي يغير العرض المعروض دون تبادل البيانات مع الخادم، على سبيل المثال سحب عنصر من شجرة لإفلاته في قائمة. يتم معالجة هذا النوع من الأحداث بالكامل محليًا داخل المتصفح.
غالبًا ما يتم تشغيل تطبيق Flex بالطريقة التالية:
![]() |
- في [1]، يتم طلب صفحة HTML
- في [2]، يتم إرسالها. وهي تحمل معها ملفًا ثنائيًا SWF (ShockWave Flash) يحتوي على التطبيق Flex بالكامل: جميع العروض ورمز إدارة الأحداث الخاصة بها. سيتم تنفيذ هذا الملف بواسطة المكون الإضافي Flash Player في المتصفح.
![]() |
- يتم تنفيذ عميل Flex محليًا على المتصفح إلا عندما يحتاج إلى بيانات خارجية. في هذه الحالة، يطلبها من الخادم [3]. ويستلمها في [4] وفقًا لتنسيقات متنوعة: XML أو ثنائية. يمكن كتابة التطبيق الذي يتم الاستعلام عنه على خادم الويب بأي لغة برمجة. ما يهم هو تنسيق الرد فقط.
لقد وصفنا بنية تشغيل تطبيق Flex حتى يدرك القارئ الفرق بينها وبين بنية تطبيق الويب التقليدي، حيث لا تحتوي الصفحات على كود (Javascript، Flex، Silverlight، ...) يقوم المتصفح بتنفيذه. في الحالة الأخيرة، يكون المتصفح سلبيًا: فهو يعرض ببساطة صفحات HTML التي تم إنشاؤها على خادم الويب الذي يرسلها إليه.
14.1. بنية تطبيق العميل/الخادم
تشبه بنية العميل/الخادم التي تم تنفيذها تلك الموجودة في الإصدارين 6 و8:
![]() |
في [1]، يتم استبدال طبقة الويب ASP.NET بطبقة ويب Flex مكتوبة بلغة MXML و ActionScript. سيتم إنشاء العميل [C] بواسطة IDE Flex Builder. تجدر الإشارة هنا إلى أن هذه البنية تتضمن خادمين ويب غير موضحين:
- خادم ويب ASP.NET الذي يقوم بتشغيل خدمة الويب [S]
- خادم ويب APACHE الذي يقوم بتشغيل عميل الويب [1]
14.2. مشروع Flex 3 للعميل
نقوم بإنشاء عميل Flex باستخدام Flex Builder 3:
![]() |
- في Flex Builder 3، نقوم بإنشاء مشروع جديد في [1]
- ونعطيه اسمًا في [2] ونحدد في [3] المجلد الذي سيتم إنشاؤه فيه
![]() |
- في [4]، نسمي التطبيق الرئيسي (الذي سيتم تنفيذه)
- في [5]، يتم إنشاء المشروع
- في [6]، الملف الرئيسي للتطبيق MXML
- يحتوي ملف MXML على عرض ورمز إدارة أحداثه. تتيح علامة التبويب [Source] [7] الوصول إلى الملف MXML. يحتوي هذا الملف على علامات <mx> التي تصف العرض بالإضافة إلى كود ActionScript.
- يمكن إنشاء العرض بيانيًا باستخدام علامة التبويب [Design] [8]. ثم يتم إنشاء علامات MXML التي تصف العرض تلقائيًا في علامة التبويب [Source]. والعكس صحيح: العلامات MXML المضافة مباشرة في علامة التبويب [Source] تنعكس بيانياً في علامة التبويب [Design].
14.3. العرض رقم 1
سنقوم تدريجياً بإنشاء واجهة ويب مشابهة لتلك الموجودة في الإصدار 1 (انظر الفقرة 4). سنقوم أولاً بإنشاء الواجهة التالية:
![]() |
- في [1]، العرض عند نجاح الاتصال بخدمة الويب. يتم عندئذٍ ملء قائمة الموظفين.
- في [2]، العرض عند فشل الاتصال بخدمة الويب. يتم عندئذٍ عرض رسالة خطأ.
الملف الرئيسي [main.xml] للعميل هو التالي:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init()">
<mx:VBox width="100%">
<mx:Label text="Feuille de salaire" fontSize="30"/>
<mx:HBox>
<mx:VBox>
<mx:Label text="Employés"/>
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>
</mx:VBox>
<mx:VBox>
<mx:Label text="Heures travaillées"/>
<mx:TextInput id="txtHeuresTravaillees"/>
</mx:VBox>
<mx:VBox>
<mx:Label text="Jours travaillés"/>
<mx:NumericStepper id="joursTravailles" minimum="0" maximum="31" stepSize="1"/>
</mx:VBox>
<mx:VBox>
<mx:Label text=""/>
<mx:Button id="btnSalaire" label="Salaire"/>
</mx:VBox>
</mx:HBox>
<mx:TextArea id="msg" minWidth="400" minHeight="100" editable="false" visible="true" enabled="true" horizontalScrollPolicy="auto" verticalScrollPolicy="auto" x="0" y="0" maxHeight="100" maxWidth="400"/>
</mx:VBox>
<mx:WebService ...>
...
</mx:WebService>
<mx:Script>
<![CDATA[
...
// البيانات
[Bindable]
private var employes : ArrayCollection;
private function init():void{
...
}
]]>
</mx:Script>
</mx:Application>
في هذا الرمز، يجب التمييز بين عدة أمور:
- تعريف التطبيق (السطران 2-3)
- وصف عرض التطبيق (الأسطر 4-25)
- مديري الأحداث بلغة ActionScript داخل العلامة <mx:Script> (الأسطر 31-42).
- تعريف خدمة الويب البعيدة (الأسطر 27-29)
لنعلق أولاً على تعريف التطبيق نفسه ووصف واجهته:
- السطران 2-3: يحددان:
- طريقة ترتيب المكونات في حاوية العرض. يشير السمة layout="vertical" إلى أن المكونات ستكون واحدة تحت الأخرى.
- الطريقة التي يجب تنفيذها عند إنشاء مثيل للواجهة، c.a.d. اللحظة التي يتم فيها إنشاء مثيل لجميع مكوناتها. تشير السمة creationComplete="init();" تشير إلى أن الطريقة init في السطر 38 هي التي يجب تنفيذها. creationComplete هي إحدى الأحداث التي يمكن أن تصدرها الفئة Application.
- تحدد الأسطر 4-25 مكونات العرض
- السطور 4-25: حاوية عمودية: سيتم وضع المكونات فيها واحدة تحت الأخرى
- السطر 5: يحدد نصًا
- الأسطر 6-23: حاوية أفقية: سيتم وضع المكونات فيها أفقيًا.
- الأسطر 7-10: حاوية عمودية ستحتوي على نص وقائمة منسدلة
- السطر 8: النص
- السطر 9: القائمة المنسدلة التي ستوضع فيها قائمة الموظفين. تشير العلامة dataProvider="{employes}" إلى مصدر البيانات التي يجب أن تملأ القائمة. هنا، سيتم ملء القائمة بالكائن employes المحدد في السطر 36. لكي نتمكن من كتابة dataProvider="{employes}"، يجب أن يحتوي الحقل employes على السمة [Bindable] (السطر 35). تسمح هذه السمة بالإشارة إلى متغير ActionScript خارج العلامة <mx:Script>. الحقل employes هو من النوع ArrayCollection، وهو نوع ActionScript يسمح بتخزين قوائم الكائنات، وهنا قائمة كائنات من النوع Employe.
- الأسطر 11-14: حاوية عمودية ستحتوي على نص ومنطقة إدخال
- السطر 12: النص
- السطر 13: منطقة إدخال ساعات العمل.
- الأسطر 15-18: حاوية عمودية ستحتوي على نص ومؤشر ترقيم
- السطر 16: النص
- السطر 17: عداد التزايد الذي سيسمح بإدخال أيام العمل.
- الأسطر 19-22: حاوية عمودية ستحتوي على نص وزر سيطلق عملية حساب راتب الشخص المحدد في القائمة المنسدلة.
- السطر 20: النص
- السطر 21: الزر.
- السطر 23: نهاية الحاوية الأفقية التي بدأت في السطر 6
- السطر 24: منطقة نصية في مكون من النوع TextArea. ستعرض رسائل الخطأ.
- السطر 25: نهاية الحاوية الرأسية التي بدأت في السطر 4
تولد الأسطر 4-25 العرض التالي في علامة التبويب [Design]:
![]() |
- [1]: تم إنشاؤه بواسطة المكون Label في السطر 5
- [2]: تم إنشاؤه بواسطة المكون ComboBox في السطر 9
- [3]: تم إنشاؤه بواسطة المكون TextInput في السطر 13
- [4]: تم إنشاؤه بواسطة المكون NumericStepper في السطر 17
- [5]: تم إنشاؤه بواسطة المكون Button في السطر 21
- [6]: تم إنشاؤه بواسطة المكون TextArea في السطر 24
لنلقِ نظرة الآن على إعلان خدمة الويب البعيدة:
<mx:WebService id="pam"
wsdl="http://localhost:1077/Service1.asmx?WSDL"
fault="wsFault(event);"
showBusyCursor="true">
<mx:operation
name="GetAllIdentitesEmployes"
result="loadEmployesCompleted(event)"
fault="loadEmployesFault(event);">
<mx:request/>
</mx:operation>
</mx:WebService>
- السطر 1: خدمة الويب هي مكون يحمل المعرف pam (السمة id)
- السطر 2: URI من ملف WSDL الخاص بخدمة الويب (انظر الفقرة 9.2)
- السطر 3: الطريقة التي يجب تنفيذها في حالة حدوث خطأ أثناء التبادل مع خدمة الويب: الطريقة wsFault.
- السطر 4: يطلب عرض مؤشر لإعلام المستخدم بأن عملية تبادل مع خدمة الويب جارية.
- الأسطر 5-10: إحدى العمليات التي توفرها خدمة الويب البعيدة. هنا، الطريقة GetAllIdentitesEmployes.
- السطر 7: الطريقة التي يجب تنفيذها عندما ينتهي استدعاء هذه الطريقة بشكل طبيعي، c.a.d. عندما ترسل خدمة الويب قائمة الموظفين بشكل صحيح
- السطر 8: الطريقة التي يجب تنفيذها عندما ينتهي استدعاء هذه الطريقة بخطأ.
- السطر 9: معلمات العملية GetAllIdentitesEmployes. نعلم أن هذه الطريقة لا تتطلب معلمات. لذلك نترك العلامة <mx:request> فارغة.
لنلقِ نظرة الآن على الكود ActionScript المرتبط بخدمة الويب:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// البيانات
[Bindable]
private var employes : ArrayCollection;
private function init():void{
// تدوين إحداثيات منطقة الرسالة
msgHeight=msg.height;
msgWidth=msg.width;
// إخفاء منطقة الرسائل
hideMsg();
// طلب إلى خدمة الويب البعيدة للحصول على قائمة مبسطة بالموظفين
pam.GetAllIdentitesEmployes.send();
}
private function wsFault(event:Event):void{
// الإبلاغ عن الخطأ
msg.text="Service distant indisponible";
showMsg();
}
private function loadEmployesCompleted(event:ResultEvent):void{
// ملء قائمة الموظفين المنسدلة
employes=event.result as ArrayCollection;
}
private function displayEmploye(employe:Object):String{
// هوية الموظف
return employe.Prenom + " " + employe.Nom;
}
private function loadEmployesFault(event:FaultEvent):void{
// عرض رسالة الخطأ
msg.text=event.fault.message;
// نموذج
showMsg();
}
// إدارة الكتل
private var msgWidth:int;
private var msgHeight:int;
private function hideMsg():void{
msg.height=0;
msg.width=0;
}
private function showMsg():void{
msg.height=msgHeight;
msg.width=msgWidth;
}
]]>
</mx:Script>
- السطر 11: يتم تنفيذ الطريقة init عند بدء تشغيل التطبيق لأننا كتبنا:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init()">
- السطران 13-14: يتم حفظ ارتفاع وعرض منطقة الرسالة. يتم استخدام طريقتين hideMsg (السطور 48-51) و showMsg (السطور 53-56) لإخفاء/إظهار منطقة الرسالة على التوالي حسب وجود خطأ أم لا. تخفي الطريقة hideMsg منطقة الرسالة عن طريق تعيين ارتفاعها وعرضها على 0. تعرض الطريقة showMsg منطقة الرسالة عن طريق إعادة تعيين ارتفاعها وعرضها إلى القيم المخزنة في الطريقة init.
- السطر 16: يتم إخفاء منطقة الرسالة. في البداية، لا يوجد خطأ.
- السطر 18: يتم استدعاء الطريقة GetAllIdentitesEmploye (السطر 6 من خدمة الويب) لخدمة الويب pam (السطر 1 من خدمة الويب). الاستدعاء غير متزامن. تشير السطر 7 من خدمة الويب إلى أن الطريقة loadEmployesCompleted ستُنفَّذ إذا انتهى هذا الاستدعاء غير المتزامن بنجاح. تشير السطر 8 من خدمة الويب إلى أن الطريقة loadEmployesFault ستُنفَّذ إذا انتهى هذا الاستدعاء غير المتزامن بفشل.
- السطر 27: الطريقة loadEmployesCompleted التي يتم تنفيذها إذا انتهى استدعاء خدمة الويب في السطر 18 بنجاح.
- السطر 29: نعلم أن خدمة الويب ترسل استجابة XML. من الجيد العودة إليها لفهم الرمز ActionScript:
![]() |
- إلى [1]، صفحة خدمة الويب [Service.asmx]
- إلى [2]، الرابط إلى صفحة اختبار الطريقة [GetAllIdentitesEmployes]
- إلى [3]، تم إجراء الاختبار. لا يُتوقع أي معلمات.
- في [4]: تحتوي الإجابة XML على جدول بالموظفين. لكل واحد منهم، لدينا خمسة معلومات مغلفة في العلامات <Id>، <Version>، <SS>، <Nom>، <Prenom>. إذا تم وضع الإجابة XML في جدول employes من النوع ArrayCollection:
- employes.getItemAt(i): هو العنصر رقم i في المصفوفة
- employes.getItemAt(i).SS: هو رقم الضمان الاجتماعي لهذا الموظف.
- employes.getItemAt(i).الاسم: هو اسم هذا الموظف
- ...
لنعد إلى الرمز ActionScript:
- السطر 29: event.result يمثل استجابة XML من خدمة الويب. تعيد الطريقة GetAllIdentitesEmployes مصفوفة من الموظفين. event.result يمثل مصفوفة الموظفين هذه. يتم وضعه في متغير من النوع ArrayCollection، وهو نوع يمثل بشكل عام مجموعة من الكائنات. يتم إعلان هذا المتغير المسمى employes في السطر 9. نتذكر أن هذا المتغير هو مصدر بيانات قائمة الموظفين المنسدلة:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>
بالنسبة لكل موظف في مصدر البيانات الخاص به، سيستدعي القائمة المنسدلة الطريقة displayEmploye (السمة labelFunction) لعرض الموظف. نرى في الأسطر 32-34 أن هذه الطريقة تعرض الاسم واللقب للموظف.
- السطر 37: الطريقة loadEmployesFault التي يتم تنفيذها في حالة فشل استدعاء خدمة الويب في السطر 18. event.fault.message هي رسالة الخطأ التي ترسلها خدمة الويب.
- السطر 39: يتم وضع رسالة الخطأ هذه في منطقة الرسائل
- السطر 41: يتم عرض منطقة الرسائل.
عند إنشاء التطبيق، يوجد الكود القابل للتنفيذ في المجلد [bin-debug] في مشروع Flex:
![]() |
أعلاه،
- يمثل الملف [main.html] الملف HTML الذي سيطلبه المتصفح من خادم الويب للحصول على عميل Flex
- الملف [main.swf] هو الملف الثنائي لعميل Flex الذي سيتم تغليفه في الصفحة HTML المرسلة إلى المتصفح ثم تنفيذه بواسطة المكون الإضافي Flash Player الخاص به.
نحن جاهزون لتشغيل عميل Flex. لكن علينا أولاً إعداد بيئة التشغيل اللازمة له. لنعد إلى بنية العميل/الخادم التي تم اختبارها:
![]() |
جانب الخادم:
- تشغيل خدمة الويب ASP.NET [S]
من جانب العميل:
- تشغيل خادم Apache الذي سيُطلب منه تشغيل تطبيق Flex.
هنا نستخدم الأداة Wamp. باستخدام هذه الأداة، يمكننا ربط اسم مستعار بالمجلد [bin-debug] الخاص بمشروع Flex.
![]() |
- يوجد رمز Wamp في أسفل الشاشة [1]
- بالنقر بزر الماوس الأيسر على أيقونة Wamp، حدد الخيار Apache [2] / Alias Directories [3, 4]
- حدد الخيار [5]: إضافة اسم مستعار
![]() |
- في [6]، قم بتعيين اسم مستعار (أي اسم) للتطبيق الويب الذي سيتم تنفيذه
- في [7] حدد جذر تطبيق الويب الذي سيحمل هذا الاسم المستعار: وهو المجلد [bin-debug] لمشروع Flex الذي أنشأناه للتو.
لنتذكر بنية المجلد [bin-debug] لمشروع Flex:
![]() |
الملف [main.html] هو الملف HTML لتطبيق Flex. بفضل الاسم المستعار الذي أنشأناه للتو على المجلد [bin-debug]، سيتم الحصول على هذا الملف من خلال URL [http://localhost/pam-v10-flex-client-webservice/main.html]. نطلب هذا الملف في متصفح مزود بمكوّن Flash Player الإصدار 9 أو أعلى:
![]() |
- في [1]، وURL من تطبيق Flex
- إلى [2]، قائمة الموظفين عندما يكون كل شيء على ما يرام
- إلى [3]، النتيجة التي يتم الحصول عليها عند إيقاف خدمة الويب
قد تشعر بالفضول لعرض شفرة المصدر للصفحة HTML المستلمة:
- يبدأ نص الصفحة في السطر 25. ولا يحتوي على HTML التقليدي بل كائن (السطر 28) من النوع "application/x-shockwave-flash" (السطر 41). هذا هو الملف [main.swf] (السطر 31) الذي يمكن رؤيته في مجلد [bin-debug] الخاص بمشروع Flex. إنه ملف كبير الحجم: حوالي 600 كيلوبايت لهذا المثال البسيط.
14.4. الطريقة رقم 2
سنقوم بإضافة حاوية جديدة من النوع VBox إلى العرض الحالي:
![]() |
![]() |
- في [4,5]، نجعل من [main2.mxml] التطبيق الافتراضي الجديد. وهو الذي سيتم تجميعه من الآن فصاعدًا.
- في [6]، يُشار إلى التطبيق الافتراضي بنقطة زرقاء.
سيعرض الحاوية [1] المعلومات المتعلقة بالموظف المحدد في القائمة المنسدلة [2]. نقوم بنسخ [main.xml] إلى [main2.xml] و [3] لإنشاء العرض الجديد. سنعمل الآن مع [main2.xml].
![]() |
التعديل الذي تم إجراؤه على المشروع السابق هو إضافة الحاوية في السطر 26 أعلاه التي تحتوي على الرمز MXML من الحاوية [1] الخاصة بالطريقة. نمنحه المعرف employe حتى نتمكن من التعامل معه عبر الكود. في الواقع، يجب أن يكون من الممكن إخفاء/إظهار هذا الحاوية باستخدام نفس التقنية المستخدمة سابقًا لمنطقة الرسائل.
لنعد إلى العرض المرئي للطريقة:
![]() |
لنحدد الحاويات المختلفة للمعلومات الجديدة المعروضة:
- V1: حاوية عمودية لجميع المكونات: التسمية Employé [1] والحاويات الأفقية [H1] و [H2]
- H1: حاوية أفقية للمعلومات Nom و Prénom و Adresse
- V2: حاوية عمودية لعبارة Nom وعرض اسم الموظف.
- H2: حاوية أفقية للمعلومات Ville، الرمز البريدي، Indice
فيما يلي الكود الكامل للحاوية "employe":
<mx:VBox id="employe" width="100%">
<mx:Label text="Employé" fontSize="20" color="#09F3EB"/>
<mx:HBox>
<mx:VBox >
<mx:Label text="Nom"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblNom" minWidth="100" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
<mx:VBox >
<mx:Label text="Prénom"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblPreNom" minWidth="100" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
<mx:VBox >
<mx:Label text="Adresse"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblAdresse" minWidth="250" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
</mx:HBox>
<mx:HBox>
<mx:VBox >
<mx:Label text="Ville"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblVille" minWidth="100" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
<mx:VBox >
<mx:Label text="Code Postal"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblCodePostal" minWidth="70" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
<mx:VBox >
<mx:Label text="Indice"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblIndice" minWidth="20" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
</mx:HBox>
</mx:VBox>
الرمز واضح بذاته. دعونا نوضح ببساطة الحاوية الرأسية التي تعرض اسم الموظف على سبيل المثال:
- الأسطر 4-9: الحاوية الرأسية
- السطر 5: العنوان Nom
- السطور 6-8: حاوية عمودية ستعرض اسم الموظف (السطر 7). نرغب في إعطاء لون خلفية مختلف للحقول التي تعرض معلومات عن الموظف. لا يوفر المكون Text هذه الإمكانية (أو ربما لم أبحث جيدًا). يمكن تحديد لون خلفية الحاوية. ولهذا السبب تم استخدامه هنا.
- السطر 7: المكون Text الذي سيعرض اسم الموظف. نحدد له ارتفاعًا وعرضًا أدنى.
سنستخدم الحاوية "employe" لعرض معلومات الموظف الذي يختاره المستخدم في قائمة الموظفين المنسدلة، وذلك بغض النظر عن الزر [Salaire] الذي سيكون دوره لاحقًا حساب الراتب بمجرد إدخال جميع المعلومات اللازمة.
لإدارة تغيير الاختيار في القائمة المنسدلة "employes"، يتغير رمزها MXML على النحو التالي:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye" change="displayInfosEmploye();"/>
يتم بث حدث التغيير من القائمة المنسدلة عندما يغير المستخدم اختياره. وسيكون مدير هذا الحدث هو الطريقة displayInfosEmploye.
لنتذكر الطرق التي يعرضها خدمة الويب البعيدة:
// قائمة بجميع هويات الموظفين
public Employe[] GetAllIdentitesEmployes();
// ------- حساب الراتب
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles);
نريد هنا عرض المعلومات (الاسم، الاسم الأول، ...) الخاصة بالموظف المحدد في القائمة المنسدلة. لا تعرض خدمة الويب أي طريقة للحصول عليها. ومع ذلك، يمكننا استخدام الطريقة GetSalaire بتمرير الرقم SS للموظف المحدد و0 لساعات وأيام العمل. سيتم إجراء حساب غير ضروري للراتب، لكن الطريقة GetSalaire ستُرجع لنا كائنًا من النوع FeuilleSalaire حيث سنجد المعلومات التي نحتاجها.
يتم تعديل الإعلان الحالي لخدمة الويب ليشمل تعريف الطريقة GetSalaire:
<mx:WebService id="pam"
wsdl="http://localhost:1077/Service1.asmx?WSDL"
fault="wsFault(event);"
showBusyCursor="true">
<mx:operation
name="GetAllIdentitesEmployes"
result="loadEmployesCompleted(event)"
fault="loadEmployesFault(event);">
<mx:request/>
</mx:operation>
<mx:operation name="GetSalaire"
result="getSalaireCompleted(event)"
fault="getSalaireFault(event);">
<mx:request>
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
</mx:request>
</mx:operation>
</mx:WebService>
- الأسطر 11-19: تعريف طريقة GetSalaire لخدمة الويب
- السطر 12: يحدد الطريقة التي يجب تنفيذها عند نجاح استدعاء الطريقة GetSalaire
- السطر 13: يحدد الطريقة التي يجب تنفيذها عند فشل استدعاء الطريقة GetSalaire
- الأسطر 14-18: تتوقع الطريقة GetSalaire ثلاثة معلمات. يتم تعريفها داخل علامة <mx:request> بالشكل <param1>قيمة1</param1>. لا يمكن أن يكون المعرف param1 أي شيء. يجب استخدام الأسماء التي تتوقعها خدمة الويب:
![]() |
- في [1]، صفحة خدمة الويب [http://localhost:1077/Service1.asmx]
- في [2]، الرابط إلى صفحة اختبار الطريقة [GetSalaire]
- في [3]، المعلمات المتوقعة من قبل الطريقة. هذه هي الأسماء التي يجب استخدامها كعلامات فرعية لعلامة <mx:request>.
لنعد إلى إعلان خدمة الويب:
<mx:operation name="GetSalaire"
result="getSalaireCompleted(event)"
fault="getSalaireFault(event);">
<mx:request>
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
</mx:request>
</mx:operation>
- السطر 5: المعلمة ss. نتذكر أنه عند بدء تشغيل تطبيق Flex، تم تخزين جدول جميع الموظفين في متغير employes من النوع ArrayCollection.
- employes.getItemAt(i): هو الموظف رقم i في المصفوفة
- employes.getItemAt(i).SS: هو رقم الضمان الاجتماعي لهذا الموظف.
- cmbEmployes.selectedIndex: هو رقم العنصر المحدد في قائمة الموظفين المنسدلة cmbemployes.
فيما سبق، كيف نعرف أن SS هو رقم الضمان الاجتماعي لموظف ما؟ للقيام بذلك، يجب الرجوع إلى الإجابة المرسلة بواسطة الطريقة GetAllIdentitesEmployes:
![]() |
- إلى [1]، صفحة خدمة الويب [Service.asmx]
- إلى [2]، الرابط إلى صفحة اختبار الطريقة [GetAllIdentitesEmployes]
- إلى [3]، تم إجراء الاختبار. لا يُتوقع أي معلمات.
- في [4]: تحتوي الإجابة XML على جدول للموظفين. وهذا الجدول هو الذي تم تخزينه في المتغير employes. نرى في [5] أن SS هو بالفعل العلامة المستخدمة لاحتواء رقم الضمان الاجتماعي.
لننهي دراسة خدمة الويب:
<mx:operation name="GetSalaire"
result="getSalaireCompleted(event)"
fault="getSalaireFault(event);">
<mx:request>
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
</mx:request>
</mx:operation>
- السطر 6: سيتم توفير عدد ساعات العمل بواسطة متغير heuresTravaillees
- السطر 6: سيتم توفير عدد أيام العمل بواسطة متغير joursDeTravail
يجب الإعلان عن هذه المتغيرات في العلامة <mx:Script> باستخدام السمة [Bindable] التي تسمح بمرجعها بواسطة مكونات MXML (الأسطر 7-10 أدناه).
<mx:Script>
<![CDATA[
...
// البيانات
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
...
</mx:Script>
يتطور كود إدارة أحداث العرض على النحو التالي:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// البيانات
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
private function init():void{
// يتم تسجيل ارتفاع/عرض # الكتل
employeHeight=employe.height;
employeWidth=employe.width;
// إخفاء بعض العناصر
hideEmploye();
...
}
private function displayInfosEmploye():void{
// نموذج
hideEmploye();
// يتم حساب راتب افتراضي
heuresTravaillees=0;
joursDeTravail=0;
pam.GetSalaire.send();
}
private function getSalaireCompleted(event:ResultEvent):void{
...
}
private function getSalaireFault(event:FaultEvent):void{
...
}
// طرق عرض جزئية -------------------------------------------------
private var employeHeight:int;
private var employeWidth:int;
private function hideEmploye():void{
employe.height=0;
employe.width=0;
}
private function showEmploye():void{
employe.height=employeHeight;
employe.width=employeWidth;
}
]]>
</mx:Script>
- السطر 15: تقوم طريقة init التي يتم تنفيذها عند بدء تشغيل تطبيق Flex بتخزين ارتفاع/عرض الحاوية الرأسية employe، وذلك من أجل التمكن من استعادتها (الأسطر 50-53) بعد إخفائها (الأسطر 45-48).
- السطر 24: الطريقة displayInfosEmploye هي الطريقة التي يتم تنفيذها عندما يغير المستخدم اختياره في قائمة الموظفين المنسدلة.
- السطر 26: يتم إخفاء الحاوية employe إذا كانت مرئية
- السطر 30: يتم استدعاء طريقة GetSalaire لخدمة الويب بشكل غير متزامن. ومن المعروف أنها تتوقع ثلاثة معلمات:
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
- السطر 1: سيكون المعلمة ss هو رقم SS للموظف المحدد في قائمة الموظفين المنسدلة
- السطر 2: تعين الطريقة displayInfosEmploye القيمة 0 للمتغير heuresTravaillees (السطر 28)
- السطر 3: تقوم الطريقة displayInfosEmploye بتعيين القيمة 0 للمتغير joursDeTravail (السطر 29)
يتم تنفيذ الطريقة GetSalaireCompleted إذا انتهت الطريقة GetSalaire لخدمة الويب بنجاح:
private function getSalaireCompleted(event:ResultEvent):void{
// إخفاء رسالة الخطأ
hideMsg();
// يتم استلام كشف الراتب
var feuilleSalaire:Object=event.result;
// العرض
lblNom.text=feuilleSalaire.Employe.Nom;
lblPreNom.text=feuilleSalaire.Employe.Prenom;
lblAdresse.text=feuilleSalaire.Employe.Adresse;
lblVille.text=feuilleSalaire.Employe.Ville;
lblCodePostal.text=feuilleSalaire.Employe.CodePostal;
lblIndice.text=feuilleSalaire.Employe.Indice;
showEmploye();
}
- السطر 3: يتم إخفاء منطقة الرسالة في حالة عرضها.
- السطر 5: يتم استرداد كشف الراتب الذي تم إرجاعه بواسطة الطريقة GetSalaire
لمعرفة ما ترسلها الطريقة GetSalaire بالضبط، نعود إلى صفحة خدمة الويب:
![]() |
- في [1]، صفحة خدمة الويب [Service.asmx]
- إلى [2]، والرابط الذي يؤدي إلى صفحة اختبار الطريقة [GetSalaire]
- في [3]، يتم توفير المعلمات
- في [4]، النتيجة XML التي تم الحصول عليها.
لنعد إلى الطريقة getSalaireCompleted:
private function getSalaireCompleted(event:ResultEvent):void{
// إخفاء رسالة الخطأ
hideMsg();
// يتم استلام كشف الراتب
var feuilleSalaire:Object=event.result;
// عرض
lblNom.text=feuilleSalaire.Employe.Nom;
lblPreNom.text=feuilleSalaire.Employe.Prenom;
lblAdresse.text=feuilleSalaire.Employe.Adresse;
lblVille.text=feuilleSalaire.Employe.Ville;
lblCodePostal.text=feuilleSalaire.Employe.CodePostal;
lblIndice.text=feuilleSalaire.Employe.Indemnites.Indice;
showEmploye();
}
- السطر 5: feuilleSalaire=event.result يمثل التدفق XML [4] الذي تم إرجاعه بواسطة الطريقة GetSalaire. بناءً على هذا التدفق، نرى أن:
- feuilleSalaire.Employe هو التدفق XML لموظف
- feuilleSalaire.Employe.Nom هو اسم هذا الموظف
- ...
- الأسطر 7-12: يتم استخدام التدفق XML feuilleSalaire لملء الحقول المختلفة للحاوية employe.
- السطر 13: يتم عرض الحاوية "employe".
يتم تنفيذ الطريقة getSalaireFault إذا انتهت الطريقة GetSalaire لخدمة الويب بشكل خاطئ:
private function getSalaireFault(event:FaultEvent):void{
// عرض رسالة الخطأ
msg.text=event.fault.message;
// نموذج
showMsg();
}
- السطر 3: يتم وضع رسالة الخطأ event.fault.message في منطقة الرسائل
- السطر 5: يتم عرض منطقة الرسائل
هنا تنتهي التعديلات اللازمة لهذه النسخة الجديدة. عند حفظها، وإذا كانت صحيحة من الناحية النحوية، يتم إنشاء النسخة القابلة للتنفيذ في مجلد [bin-debug] الخاص بالمشروع:
![]() |
أعلاه، [main2.html] هي الصفحة HTML التي تحتوي على ملف التطبيق الثنائي Flex [main2.swf] الذي سيتم تنفيذه بواسطة Flash Player.
يمكننا اختبار هذا الإصدار الجديد:
- يجب تشغيل خدمة الويب ASP.NET
- يجب تشغيل خادم Apache للعميل Flex
إذا افترضنا أن الاسم المستعار [pam-v10-flex-client-webservice] المستخدم في الإصدار السابق لا يزال موجودًا، فإننا نطلب من خادم Apache URL [http://localhost/pam-v10-flex-client-webservice/main2.html] في متصفح:
![]() |
![]() |
- في [1]، URL المطلوب
- إلى [2]، قائمة الموظفين
- في [3]، يتم تغيير التحديد في القائمة المنسدلة لإحداث الحدث change
- إلى [4]، والنتيجة التي تم الحصول عليها: سجل جوستين لافيرتي.
14.5. الطريقة رقم 3
تقوم العرض رقم 3 بالتحقق من صحة النموذج. هنا، يتم التحقق فقط من حقل الإدخال "txtHeuresTravaillees". طالما أن النموذج غير صحيح، سيظل الزر "btnSalaire" معطلاً.
لإضافة هذه الوظيفة، نقوم بنسخ [main2.mxml] في [main3.mxml]:
![]() |
من الآن فصاعدًا، سنعمل مع [main3.mxml] الذي سنجعله التطبيق الافتراضي (انظر هذا المفهوم في الفقرة 14.4). أولاً، نضيف سمة إلى المكون "txtHeuresTravaillees":
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
في كل مرة يتغير فيها محتوى حقل الإدخال "txtHeuresTravaillees"، يتم استدعاء الطريقة validateForm. وهي طريقة محلية كتبها المطور. في هذه الطريقة، يمكننا التحقق من أن محتوى حقل الإدخال "txtHeuresTravaillees" هو بالفعل عدد صحيح موجب. سنقوم بذلك بطريقة أخرى باستخدام مكون للتحقق من الصحة:
<mx:NumberValidator id="heuresTravailleesValidator" source="{txtHeuresTravaillees}" property="text"
precision="2" allowNegative="false"
invalidCharError="Caractères invalides"
precisionError="Deux chiffres au plus après la virgule"
negativeError="Le nombre d'heures doit être positif ou nul"
invalidFormatCharsError="Format invalide"
required="true"
requiredFieldError="Donnée requise"/>
- السطر 1: تسمح المكونة <mx:NumberValidator> بالتحقق من أن مكونة أخرى تحتوي على عدد صحيح أو عدد حقيقي.
- السطر 1: يعطي السمة id معرفًا للمكون.
- السطر 1: source هو معرف المكون الذي يتم التحقق منه بواسطة المكون NumberValidator. هنا، يتم التحقق من حقل الإدخال "txtHeuresTravaillees".
- السطر 1: property هو اسم خاصية المكون source التي تحتوي على القيمة المراد التحقق منها. في النهاية، يتم التحقق من القيمة source.property، وهي هنا txtHeuresTravaillees.text.
- السطر 2: precision يحدد الحد الأقصى لعدد الخانات العشرية المسموح بها. precision=0 يعادل التحقق من أن الرقم المدخل هو عدد صحيح.
- السطر 2: allowNegative يشير إلى ما إذا كانت الأرقام السالبة مسموحة أم لا
- السطر 7: required يشير إلى ما إذا كان الإدخال إلزاميًا أم لا.
عندما لا يتم التحقق من شرط التحقق من الصحة، يتم عرض رسالة خطأ في فقاعة بالقرب من المكون الخاطئ. بشكل افتراضي، تكون هذه الرسائل باللغة الإنجليزية. من الممكن تعريف هذه الرسائل بنفسك:
- (تابع)
- invalidCharError: رسالة الخطأ عندما يحتوي النص على حرف لا يمكن وجوده في رقم
- precisionError: رسالة الخطأ عندما يكون عدد الخانات العشرية غير صحيح بالنسبة للسمة precision
- negativeError: رسالة الخطأ عندما يكون الرقم سالبًا في حين أن السمة allowNegative="false"
- requiredFieldError: رسالة الخطأ عند عدم وجود إدخال مع وجود السمة requiredField="true"
- invalidFormatCharsError: رسالة الخطأ عندما يحتوي النص على أحرف أو تنسيق غير صالح؟
لنعد إلى المكون "txtHeuresTravaillees":
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
قد تكون الطريقة validateForm كما يلي في العلامة <mx:Script>:
private function validateForm(event:Event):void
{
// يتم التحقق من ساعات العمل
var evt:ValidationResultEvent = heuresTravailleesValidator.validate();
// هل تمت المصادقة بنجاح؟
btnSalaire.enabled=evt.type==ValidationResultEvent.VALID;
}
- السطر 4: يتم تنفيذ أداة التحقق "heuresTravailleesValidator". وتُرجع نتيجة من النوع ValidationResultEvent.
- السطر 6: evt.type هو من النوع String ويشير إلى نوع الحدث. يحتوي evt.type على قيمتين محتملتين لنوع ValidationResultEvent، وهما "invalid" أو "valid" ممثلتان بالثوابت ValidationResultEvent.INVALID و ValidationResultEvent.VALID. إذا كانت السطر 4، وتم التحقق بنجاح، يجب أن تكون قيمة evt.type هي ValidationResultEvent.VALID. في هذه الحالة، يتم تنشيط الزر btnSalaire وإلا يتم تعطيله.
وهذا يكفي لاختبار صحة ساعات العمل.
![]() |
فيما سبق، أنتجت تجميع المشروع الملفين [main3.html] و [main3.swf]. نطلب URL [http://localhost/pam-v10-flex-client-webservice/main3.html] في متصفح ونقوم بالتحقق من حالات خطأ متنوعة:
![]() |
![]() |
- يظهر الحقل الخاطئ بإطار أحمر [1, 2, 3]، بينما يظهر الحقل الصحيح بإطار أزرق [4].
- في [4]، نلاحظ أن الزر [Salaire] نشط لأن عدد ساعات العمل صحيح.
14.6. الطريقة رقم 4
تُنهي العرض رقم 4 نموذج حساب الراتب. وللقيام بذلك، نقوم بنسخ [main3.xml] في [main4.xml] ونعمل الآن مع main4 الذي نجعله التطبيق الافتراضي (انظر الفقرة 14.4).
![]() |
التغييرات التي تم إجراؤها في [main4.xml] و [1] هي التالية:
- تمت إضافة حاوية رأسية جديدة إلى العرض [2] لعرض عناصر راتب الموظف
- تمت إضافة مكون يسمح بتنسيق القيم النقدية [3]
- يتم عرض عناصر الراتب بواسطة المدير المرتبط بحدث "النقر" على الزر "btnSalaire".
تتطور طريقة العرض على النحو التالي:
![]() |
يتبع الحاوية الجديدة مبدأ الحاوية السابقة. وهي عبارة عن حاوية عمودية VBox [V1] تحتوي على أربع حاويات أفقية HBox [Hi]. تتكون الحاويات الأفقية H1 إلى H3 من حاويات عمودية تحتوي على تسميتين، حيث توجد الثانية منها في حاوية عمودية لتوفير لون خلفية.
السؤال 1: اكتب حاوية الراتب. سيُطلق عليها لاحقًا اسم complements.
السؤال 2: اكتب الطرق التي تسمح بإخفاء/إظهار الحاوية complements. سنستلهم من ما تم عمله سابقًا للحاوية employe.
نربط معالجًا بحدث "click" للزر "btnSalaire":
<mx:Button id="btnSalaire" label="Salaire" click="calculerSalaire()"/>
الطريقة calculerSalaire هي كما يلي:
private function calculerSalaire():void{
// تحضير النموذج
affichageSalaire=true;
msg.text="";
// معلمات حساب الراتب
heuresTravaillees=Number(txtHeuresTravaillees.text);
joursDeTravail=int(joursTravailles.value);
// يتم طلب الراتب من خدمة الويب
pam.GetSalaire.send();
}
- السطر 3: تُستخدم القيمة المنطقية affichageSalaire للإشارة إلى ما إذا كان يجب عرض الحاوية complements التي تعرض عناصر الراتب أم لا. يتم تنفيذ الطريقة getSalaireCompleted في حالتين:
- تغيير الموظف في قائمة الموظفين المنسدلة لعرض معلوماته بدون الراتب. عندئذٍ نضع affichageSalaire=false.
- حساب الراتب
- السطر 6: يتم تحويل نص حقل الإدخال txtHeuresTravaillees إلى رقم حقيقي.
- السطر 7: يتم تحويل قيمة العداد joursTravailles إلى عدد صحيح.
- السطر 9: استدعاء الطريقة البعيدة GetSalaire. يُذكر أن هذه الطريقة تتوقع ثلاثة معلمات، منها المعلمتان heuresTravaillees و joursDeTravail اللتان تم تهيئتهما في السطرين 6 و 7. ويُذكر أيضًا أنه في حالة نجاح الاستدعاء غير المتزامن للطريقة GetSalaire:
- نجح، فسيتم استدعاء الطريقة getSalaireCompleted
- فشلت، سيتم استدعاء الطريقة getSalaireFault
السؤال 3: استكمال الإجراء الحالي getSalaireCompleted بحيث يعرض راتب الموظف في حالة النقر على الزر btnSalaire.
حاليًا، يتم عرض عناصر الراتب بدون علامة اليورو. يمكن تضمينها في الكود أو استخدام مُشكّل. وهذا ما يُقترح الآن. سيكون المُشكّل كما يلي:
<mx:CurrencyFormatter id="eurosFormatter" precision="2"
currencySymbol="€" useNegativeSign="true"
alignSymbol="right"/>
- السطر 1: id هو معرف المنسق، و precision هو عدد الخانات العشرية المطلوب الاحتفاظ بها.
- السطر 2: currencySymbol هو الرمز النقدي الذي يجب استخدامه. useNegativeSign يشير إلى ما إذا كان يجب استخدام علامة - للقيم السالبة أم لا.
- السطر 3: alignSymbol يشير إلى مكان وضع الرمز النقدي بالنسبة للرقم.
يُستخدم هذا المُشكّل في كود البرنامج النصي بالطريقة التالية:
- eurosFormatter هو معرف المنسق المطلوب استخدامه
- format هي الطريقة التي يجب استدعاؤها لتنسيق رقم. وهي تُرجع سلسلة أحرف.
- feuilleSalaire.Indemnites.BaseHeure هو الرقم المراد تنسيقه.
- lblSH هو اسم مكون من نوع Text.
السؤال 4: قم بتعديل الدالة getSalaireCompleted بحيث تستخدم مُنسق العملة.





























