Download as pdf or txt
Download as pdf or txt
You are on page 1of 190

‫أساسيات إطار العمل ‪Vue.

js‬‬

‫مقدمة إلى بناء واجهات المواقع وتطبيقات الويب عبر ‪Vue.js‬‬

‫تأليف‬

‫حسام برهان‬

‫تحرير وإشراف‬

‫جميل بيلوني‬
‫جميع الحقوق محفوظة © ‪ 2021‬أكاديمية حسوب‬

‫النسخة األوىل ‪v1.0‬‬

‫الم ِّ‬
‫صنف ‪ -‬غري تجاري ‪ -‬الرتخيص‬ ‫َّ‬
‫مرخص بموجب رخصة المشاع اإلبداعي‪َ :‬نسب ُ‬ ‫هذا العمل‬

‫بالمثل ‪ 4.0‬دويل‬
‫عن النارش‬
‫ُأ نتج هذا الكتاب برعاية شركة حسوب وأكاديمية حسوب‪.‬‬

‫تهدف أكاديمية حسوب إلى توفير فصول وكتب عالية الجودة في مختلف المجاالت وتق دم دورات ش املة‬
‫ُّ‬
‫لتعلم البرمج ة بأح دث تقنياته ا معتم د ًة على التط بيق العملي ال ذي يؤه ل الط الب ل دخول س وق‬

‫العمل بثقة‪.‬‬

‫حسوب مجموعة تقنية في مهمة لطوير العالم العربي‪ .‬تب ني حس وب منتج ات تركِّز على تحس ين مس تقبل‬

‫العمل‪ ،‬والتعليم‪ ،‬والتواصل‪ .‬تدير حسوب أكبر منصتي عمل حر في العالم العربي‪ ،‬مستقل وخمسات ويعم ل في‬

‫فيها فريق شاب وشغوف من مختلف الدول العربية‪.‬‬


‫جدول المحتويات‬

‫‪9‬‬ ‫تقديم‬

‫‪11‬‬ ‫‪ .1‬مدخل إلى ‪Vue.js‬‬

‫‪12‬‬ ‫‪ 1.1‬ماهو إطار العمل ‪ framework‬في ‪JavaScript‬؟‬

‫‪12‬‬ ‫‪ 1.2‬تطبيقك األول مع ‪Vue.js‬‬

‫‪15‬‬ ‫األول‬
‫‪ 1.3‬إضافة مزايا جديدة لتطبيقنا ّ‬
‫‪17‬‬ ‫‪ 1.4‬العمل على حاسوب محلي‬

‫‪18‬‬ ‫‪ 1.5‬ختام الفصل‬

‫‪18‬‬ ‫‪ 1.6‬تمارين داعمة‬

‫‪21‬‬ ‫‪ .2‬التعامل مع ‪DOM‬‬

‫‪22‬‬ ‫‪ 2.1‬فهم قوالب ‪Vue.js‬‬

‫‪22‬‬ ‫‪ 2.2‬الوصول إلى البيانات والتوابع من كائنات ‪Vue.js‬‬

‫‪23‬‬ ‫‪ 2.3‬الربط مع السمات ‪Attributes‬‬

‫‪25‬‬ ‫‪ 2.4‬كتابة شيفرة ‪ HTML‬خام‬

‫‪26‬‬ ‫‪ 2.5‬التعامل مع األحداث (‪)Events‬‬

‫‪31‬‬ ‫‪ 2.6‬استخدام الربط ثنائي االتجاه‬

‫‪32‬‬ ‫‪ 2.7‬ختام الفصل‬

‫‪32‬‬ ‫‪ 2.8‬تمارين داعمة‬

‫‪34‬‬ ‫‪ .3‬الموجهات الشـرطية والتكرارية‬


‫‪35‬‬ ‫‪ 3.1‬كتابة شفرات ‪ JavaScript‬مباشرة ضمن القوالب‬

‫‪36‬‬ ‫‪ 3.2‬التصييـر الشـرطي باستخدام ‪ v-if‬و ‪ v-else‬و ‪v-else-if‬‬

‫‪38‬‬ ‫‪ 3.3‬الفرق بين ‪ v-if‬و ‪v-show‬‬

‫‪40‬‬ ‫‪ 3.4‬تصييـر القوائم باستخدام ‪v-for‬‬


‫أساسيات إطار العمل ‪Vue.js‬‬ ‫جدول المحتويات‬

‫‪41‬‬ ‫‪ 3.5‬المرور على خصائص كائن‬

‫‪44‬‬ ‫‪ 3.6‬ختام الفصل‬

‫‪44‬‬ ‫‪ 3.7‬تمارين داعمة‬

‫‪45‬‬ ‫‪ .4‬المزيد حول كائن ‪Vue.js‬‬

‫‪46‬‬ ‫‪ 4.1‬إنشاء أكثـر من كائن ‪Vue.js‬‬

‫‪48‬‬ ‫‪ 4.2‬الوصول إلى عناصر ‪ HTML‬مباشرة‬

‫‪49‬‬ ‫‪ 4.3‬الخصائص المحسوبة في ‪Vue.js‬‬

‫‪51‬‬ ‫‪ 4.4‬الخصائص المراقبة ضمن كائن ‪Vue.js‬‬

‫‪53‬‬ ‫‪ 4.5‬تثبيت قالب جديد باستخدام )(‪$mount‬‬

‫‪54‬‬ ‫‪ 4.6‬فصل القالب عن عنصـر ‪ HTML‬المستهدف‬

‫‪56‬‬ ‫‪ 4.7‬ما هو المكون (‪)Component‬؟‬

‫‪59‬‬ ‫‪ 4.8‬ختام الفصل‬

‫‪59‬‬ ‫‪ 4.9‬تمارين داعمة‬

‫‪60‬‬ ‫‪ .5‬مدخل إلى التعامل مع المكونات‬


‫‪61‬‬ ‫‪ 5.1‬تجهيـز هيكل التطبيق على حاسوب محلي‬

‫‪65‬‬ ‫‪ 5.2‬بناء مكون جديد‪ :‬مكون المهام‬

‫‪70‬‬ ‫‪ 5.3‬تحسين تجربة االستخدام للمكون‬

‫‪71‬‬ ‫‪ 5.4‬تمرير وسائط إلى المكونات‬

‫‪73‬‬ ‫‪ 5.5‬إنشاء أكثر من نسخة من المكون ضمن نفس الصفحة‬

‫‪76‬‬ ‫‪ 5.6‬إضافة ميـزة الترشيح لمكون المهام‬

‫‪78‬‬ ‫‪ 5.7‬إضافة ميزة مهمة جديدة لمكون المهام‬

‫‪81‬‬ ‫‪ 5.8‬ختام الفصل‬

‫‪81‬‬ ‫‪ 5.9‬تمارين داعمة‬

‫‪82‬‬ ‫‪ .6‬التعمق في مكونات ‪Vue.js‬‬

‫‪6‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫جدول المحتويات‬

‫‪83‬‬ ‫‪ 6.1‬بناء تطبيق نموذجي (مشروبات حسوب)‬

‫‪88‬‬ ‫‪ 6.2‬إضافة وسائل تنقيح متطورة لتطبيقات ‪Vue.js‬‬

‫‪89‬‬ ‫‪ 6.3‬المكونات المتداخلة‬

‫‪91‬‬ ‫محليا وتسجيلها على المستوى العام‬


‫ً‬ ‫‪ 6.4‬تسجيل المكونات‬

‫‪93‬‬ ‫‪ 6.5‬تحديد المكون الذي اختاره المستخدم‬

‫‪96‬‬ ‫‪ 6.6‬التخاطب بين المكونات باستخدام أحداث مخصصة‬

‫‪100‬‬ ‫‪ 6.7‬ختام الفصل‬

‫‪101‬‬ ‫‪ .7‬إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫‪102‬‬ ‫‪ 7.1‬لماذا ‪Vue CLI‬؟‬

‫‪102‬‬ ‫‪ 7.2‬إعداد ‪Vue CLI‬‬

‫‪104‬‬ ‫‪ 7.3‬إنشاء مشروع جديد باستخدام ‪Vue CLI‬‬

‫‪107‬‬ ‫‪ 7.4‬نظرة عامة على هيكل المشروع‬

‫‪117‬‬ ‫‪ 7.5‬ختام الفصل‬

‫‪118‬‬ ‫‪ .8‬التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬


‫‪119‬‬ ‫‪ 8.1‬بناء هيكل تطبيق بسيط‬

‫‪124‬‬ ‫‪ 8.2‬استخدام إطار العمل ‪Bootstrap‬‬

‫‪125‬‬ ‫‪ 8.3‬استخدام بنـى معطيات متقدمة مع عناصر اإلدخال النصية‬

‫‪126‬‬ ‫‪ 8.4‬المعدالت (‪ )Modifiers‬في ‪Vue.js‬‬

‫‪127‬‬ ‫‪ 8.5‬التعامل مع مربعات االختيار وأزرار االنتقاء‬

‫‪128‬‬ ‫‪ 8.6‬التعامل مع القائمة المنسدلة‬

‫‪129‬‬ ‫‪ 8.7‬إرسال البيانات‬

‫‪130‬‬ ‫‪ 8.8‬ختام الفصل‬

‫‪131‬‬ ‫‪ .9‬المرشحات ‪ Filter‬والـمخاليط ‪Mixin‬‬

‫‪132‬‬ ‫‪ 9.1‬المرشح (‪)Filter‬‬

‫‪7‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫جدول المحتويات‬

‫‪134‬‬ ‫‪ 9.2‬المخلوط (‪)Mixin‬‬

‫‪139‬‬ ‫‪ 9.3‬ختام الفصل‬

‫‪140‬‬ ‫‪ .10‬استخدام ‪ Vue.js‬لالتصال باإلنترنت‬


‫‪141‬‬ ‫‪ 10.1‬إنشاء قاعدة بيانات على ‪Google Firebase‬‬

‫‪145‬‬ ‫‪ 10.2‬تثبيت مكتبة االتصال باإلنترنت ‪vue-resource‬‬

‫‪146‬‬ ‫‪ 10.3‬بناء تطبيق للقراءة واإلضافة من وإلى قاعدة البيانات‬

‫‪155‬‬ ‫‪ 10.4‬إضافة ميزة تعديل البيانات للتطبيق السابق‬

‫‪162‬‬ ‫‪ 10.5‬ختام الفصل‬

‫‪163‬‬ ‫‪ .11‬بناء تطبيقات ذات صفحة واحدة‬


‫‪164‬‬ ‫‪ 11.1‬بناء هيكل التطبيق والتعرف على أجزائه الرئيسية‬

‫‪165‬‬ ‫‪ 11.2‬تحويل تطبيق إلى تطبيق ‪SPA‬‬

‫‪182‬‬ ‫‪ 11.3‬ختام الفصل‬

‫‪183‬‬ ‫‪ .12‬نشر تطبيق ‪ Vue.js‬إلى اإلنتـرنت‬


‫‪183‬‬ ‫‪ 12.1‬تجهيز التطبيق قبل النشر‬

‫‪184‬‬ ‫‪ 12.2‬نشر التطبيق على منصة ‪Netlify‬‬

‫‪189‬‬ ‫‪ 12.3‬ختام الفصل‬

‫‪8‬‬
‫تقديم‬

‫ً‬
‫بداية تختلف عن غيره من الكتب التي عملنا عليها‪ ،‬إذ كنت أتخ ير كتا ًب ا عن إط ار‬ ‫بدأت حكاية هذا الكتاب‬

‫العم ل ‪ Vue.js‬من المراج ع األجنبي ة الح رة أو مفتوح ة المص در لترجمته ف وقعت ي دي على ك تيب‬

‫غيرا ال يلم ح تى باألساس يات‬


‫باسم ‪ The Vue Handbook‬لمؤلفه ‪ Flavio Copes‬ولكنه كتابًا مختص ًرا ص ً‬
‫وإنما هو تقديم أو تعريف مختصر بإطار العمل ‪ Vue.js‬فاقترح حسام بره ان ‪-‬مؤل ف ه ذا الكت اب‪ -‬العم ل على‬

‫تأليف كتاب كامل يشمل أساسيات ‪ Vue.js‬ويكون مقدمة لمن يريد تعلم هذا اإلطار‪ ،‬وهنا بدأت الحكاية وكتبن ا‬

‫معً ا فهرس الكتاب الذي بين يديك اآلن وشرعنا بالعمل‪.‬‬

‫سبب اختيارنا شرح إطار العمل ‪ Vue.js‬هو أنه إط ار ش امل واعد س ريع األداء ذاع الص يت يس تعمل لبن اء‬

‫تطبيقات ويب‪ ،‬توجهت أنظارنا إلضافة محتوى عربي مميز يشمله بع د أن اخترن ا كتا ًب ا لتغطي ة مكتب ة ‪React‬‬

‫ً‬
‫مسبقا وكتابًا حول ‪ Angular‬في خطة إلضافة مصادر تعليمي ة عالي ة الج ودة تش رح أش هر أط ر لغ ة‬ ‫الشهيرة‬

‫جافاسكربت لبناء الواجهات األمامية للمواقع وتطبيقات الويب‪.‬‬

‫حرصنا في هذا الكتاب أن يك ون س هل الفهم والتط بيق يش مل أساس يات ‪ Vue.js‬ال تي س تمكنك من ب دء‬

‫العمل سريعً ا مع هذا اإلطار متبعين قاعدة ‪( 20/80‬التي ننتهجه ا ع اد ًة في كتبن ا وال تي تق ول تعلم ‪ %20‬من‬

‫تل ك المف اهيم ال تي تس تعمل في ‪ %80‬من ح االت االس تخدام)‪ .‬اتبعن ا في الكت اب النهج العملي التط بيقي إذ‬
‫ستبني خالل فصول الكتاب عدة تطبيقات عملية وستتعلم كيفية نشرها وإطالقها على ال ويب‪ ،‬واعتم دنا ً‬
‫أيض ا‬

‫األسلوب السهل البسيط المختصر فخير الكالم ما قل ودل مع إضافة تمارين وأفكار في نهاية كل فصل ما أمكن‬

‫لتطبيقها بنفسك‪.‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫تقديم‬

‫حتى تس تفيد أك بر اس تفادة من الكت اب‪ ،‬يجب أن تمل ك معرف ة أساس ية مس بقة بلغ ة ‪ HTML‬ولغ ة ‪CSS‬‬

‫ومعرفة جيدة بلغة جافاسكربت‪ ،‬إذ يركز الكتاب على إطار العمل ‪ Vue.js‬الذي هو أحد أط ر عم ل جافاس كربت‬

‫ولن يتطرق الكت اب إلى ش رح لغ ة جافاس كربت‪ ،‬وق د تج د ص عوبة في فهم أج زاء الش يفرات واالس تفادة من‬

‫الكتاب دون تلك المعرفة المسبقة‪ .‬انظر قسم الكتب البرمجية في أكاديمية حسوب لتنزيل مصادر لتلك اللغات‪.‬‬

‫يبدأ الكتاب فصوله األولى بشرح مفهوم إطار العمل والتعرف على ‪ Vue.js‬وأخذك سريعً ا وباختص ار لب دء‬

‫استعماله ببناء أول تطبيق ع بره‪ .‬س تبدأ من الفص ل الث اني في ‪ Vue.js‬ب التعرف على إط ار العم ل ‪ Vue.js‬عن‬

‫قرب وعن آلية عمله وكيفية استعماله في بناء الوجهات األمامي ة ثم تنتق ل بع دها إلى التع رف على الموجه ات‬

‫الشرطية والتكرارية واستعمالها في قوالب الواجه ة األمامي ة‪ ،‬يليه ا ال دخول إلى ع الم مكون ات ‪ Vue.js‬وتعلم‬

‫كيفية بناءها‪ .‬ستبدأ بعدها في الفصل السابع األمور المتقدمة إذ ستتعلم التعامل م ع س طر أوام ر ‪ Vue.js‬لبن اء‬

‫المش اريع والتطبيق ات بس هولة ويس ر ثم س تتعرف بع د ذل ك على مف اهيم متقدم ة في ‪ Vue.js‬وكيفي ة بن اء‬

‫تطبيقات ذات صفحة واحدة‪ .‬ستتعلم في الفصول األخيرة من الكتاب كيفية رب ط تطبيق ك ب اإلنترنت وإض افة‬

‫قاعدة بيانات له وإطالق إلى العالم الخارجي متحرين استعمال أشهر منصات النشر وأيسرها تعاماًل ‪.‬‬

‫قيم ح ول ‪ Vue.js‬يفي دك في تعلم وب دء اس تعمال ذل ك‬


‫نرجو أن نكون قد وفقنا في إضافة مرج ع ع ربي ِّ‬
‫اإلطار لبناء تطبيقات ويب مفيدة‪ ،‬وندعو لك بالتوفيق والسداد في خطوتك القادمة وحياتك المهنية‪ ،‬وس نكون‬

‫فخورين بما ستبنيه مما تعلمته من هذا الكتاب!‬

‫نسعد بالسماع منك ألي رأي أو تصحيح أو مالحظة عبر بريد األكاديمية ‪academy@hsoub.com‬‬

‫جميل بيلوني‬

‫‪16/06/2021‬‬

‫‪10‬‬
‫‪ .1‬مدخل إىل ‪Vue.js‬‬

‫ّ‬
‫سنتعلم فيه كيف سنتعامل مع إطار العمل ‪ .Vue.js‬هذا اإلطار الواعد ال ذي‬ ‫مرح ًبا بك في هذا الكتاب التي‬

‫نتدرج في‬
‫ّ‬ ‫يُ سهّ ل العمل في تطوير الواجهة األمامي ة للتطبيق ات ‪ Frontend Applications‬إلى ح دّ بعي د‪ .‬س‬

‫فص وله بش كل س لس‪ ،‬حيث س نتناول موض وعات بس يطة في البداي ة‪ ،‬ثم وم ع تق دّ منا في الفص ول س نغطي‬

‫موضوعات أوسع وأشمل‪.‬‬

‫ستالحظ وجود تطبيقات بسيطة في كل فصل‪ ،‬نش رح من خالله ا فك رة أو أفك ار مح دّ دة‪ .‬ولكن في نهاي ة‬
‫ً‬
‫مسبقا‪ .‬الهدف من الكتاب ه و أن‬ ‫تمت تغطيتها‬ ‫الكتاب سنختم بتطبيقين عمليين‪ّ ،‬‬
‫يوضحان معظم األفكار التي ّ‬

‫مرجعي شامل إلطار العمل ‪.Vue.js‬‬


‫ّ‬ ‫تنطلق مع ‪ Vue.js‬بسهولة ويُ سر‪ ،‬وبالتالي فهو كتاب‬

‫سأفترض أ ّنه لديك معرف ة جي دة بلغ ة ‪ JavaScript‬و بلغ ة ‪ً HTML‬‬


‫أيض ا‪ ،‬كم ا ويُ ّ‬
‫فض ل أن تمتل ك معرف ة‬

‫أساسية بلغة ‪.CSS‬‬


‫ّ‬

‫سنتطرق في هذا الفصل إلى المواضيع التالية‪:‬‬

‫ماهو إطار العمل ‪ framework‬في ‪JavaScript‬؟‬ ‫•‬

‫األول مع ‪Vue.js‬‬
‫تطبيقك ّ‬ ‫•‬

‫األول‬
‫إضافة مزايا جديدة لتطبيقنا ّ‬ ‫•‬

‫ّ‬
‫محلي‬ ‫العمل على حاسوب‬ ‫•‬

‫تمارين داعمة‬ ‫•‬


‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل ‪Vue.js‬‬

‫‪ 1.1‬ماهو إطار العمل ‪ framework‬في ‪JavaScript‬؟‬

‫عموم ا في إنش اء تطبيق ات عص رية حديث ة‪،‬‬


‫ً‬ ‫ببساطة و بمعزل عن أي تفاصيل تقنية‪ ،‬يساعدنا إطار العم ل‬
‫حيث يحت وي على األدوات الالزم ة لتس هيل حي اة الم برمجين في إنش اء تطبيق ات ّ‬
‫معق دة ب زمن قياس ي‪ ،‬فال‬

‫يحتاج المبرمج أن يستخدم األسلوب التقليدي القديم في التعامل مع نموذج كائنات المستند ‪ DOM‬باستخدام‬

‫‪ ،JavaScript‬حيث يمكن باستخدام إطار العمل التعامل مع العناص ر في ‪ DOM‬بش كل قياس ي ومج ّرد وس هل‬

‫إلى حدّ بعيد‪.‬‬

‫بدأ العمل م ع ‪ JavaScript‬بتط وير مكتب ات ش هيرة مث ل ‪ jQuery‬و ‪ ،Mootools‬حيث س هّ لت مث ل ه ذه‬


‫المكتبات العمل مع ‪ JavaScript‬على نحو كب ير‪ّ ،‬‬
‫وقللت مش اكل التوافقي ة بين المتص فحات المختلف ة ال تي لم‬

‫تكن تتبع معيا ًرا موحدًّ ا‪.‬‬

‫تطوّ رت األمور بعد ذل ك لتب دأ ُأ ط ر عم ل متكامل ة في الظه ور يمكن أن ّ‬


‫نقس مها إلى مرحل تين أساس يتين‪.‬‬

‫المرحل ة األولى ك انت م ع ظه ور ُأ ط ر عم ل مث ل ‪ Backbone‬و ‪ Ember‬و ‪ Knockout‬و ‪ّ .AngularJS‬‬


‫أم ا‬

‫المرحلة الثانية وهي المرحلة الحالية فكانت مع أطر عمل مثل ‪ React‬و ‪ Angular‬و ‪.Vue‬‬

‫ٌتعدّ ‪ Vue.js‬من أحدث ُأ طر العم ل في ‪ ،JavaScript‬وهي إط ار عم ل واع د‪ ،‬يجم ع بين الس هولة والمرون ة‬

‫والقوة‪ ،‬باإلضافة إلى صغر حجم الملف الخاص بها (فقط ‪ 26‬كيلو ب ايت) مم ا يجع ل تحمي ل ص فحات ال ويب‬

‫سهلة إلى حد بعيد‪ .‬سنهتم باإلصدار ‪ 2‬من ‪ Vue.js‬مع التأكي د ب أنّ م ا س نأخذه هن ا يمكن تطبيق ه بس هولة في‬

‫إصدارات الحقة‪ ،‬إذ نزل اإلصدار ‪ 3‬بعد االنتهاء من الكتاب ولكن ُذ ِكر أنه عملية إع ادة كتاب ة لإلط ار وليس في ه‬

‫اختالفات جوهرية عن اإلصدار ‪ 2‬السابق‪.‬‬

‫‪ 1.2‬تطبيقك األول مع ‪Vue.js‬‬

‫قبل البدء باستخدام ‪ Vue.js‬علينا أن نحصل على الملف الخاص به‪ .‬لدينا عدّ ة خيارات لذلك‪ ،‬سأختار أح د‬

‫أسهل الخي ارات وه و الحص ول على المل ف الخ اص بإط ار العم ل عن طري ق ش بكة تس ليم محت وى ‪ ،CDN‬ثم‬

‫نتحدّ ث عن الخيارات األخرى فيما بعد‪ .‬يمكنك استخدام رابط ‪ CDN‬الت الي للحص ول على ‪Vue.js‬؛ الراب ط ه و‬

‫حاليا إلى الرابط فقط‪.‬‬


‫ً‬ ‫‪ unpkg.com/vue@2.6.11/dist/vue.js‬سنحتاج‬

‫انتقل اآلن إلى الموقع ‪ jsfiddle.net‬الذي ّ‬


‫يوفر بيئة تطوير بسيطة وممتازة لتجريب ‪ Vue.js‬دون تحمي ل‬

‫أي شيء على حاسوبك‪ .‬ستحصل على شكل شبيه بما يلي‪:‬‬

‫‪12‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل ‪Vue.js‬‬

‫انق ر على الراب ط ‪ URL‬ال ذي يظه ر في القس م األيس ر في األعلى من الناف ذة الس ابقة‪ ،‬بج وار‬

‫كلمة ‪:Resources‬‬

‫بعد نقر الرابط السابق ستحصل على مربّع نص‪ ،‬انسخ فيه رابط ‪ CDN‬السابق‪ ،‬ثم انق ر ال زر المج اور ال ذي‬

‫يظهر على شكل إشارة زائد‪:‬‬

‫المصغرة أ ّننا بصدد استخدام الملف الخاص بإطار العمل ‪.Vue.js‬‬


‫ّ‬ ‫نكون بذلك قد أخبرنا بيئة التطوير‬

‫انسخ اآلن شيفرة ‪ HTML‬التالي إلى القسم الخاص بأكواد ‪ HTML‬من الصفحة الرئيسية لموقع ‪:jsfiddle‬‬

‫>"‪<div id="app‬‬
‫}} ‪{{ message‬‬
‫>‪<\div‬‬

‫ثم انسخ ش يفرة ‪ JavaScript‬التالي ة والص قها ض من القس م الخ اص بش يفرات ‪ JavaScript‬في الص فحة‬

‫الرئيسية الموقع‪:‬‬

‫‪13‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل ‪Vue.js‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪message: 'Hello Vue‬‬
‫}‬
‫)}‬

‫انقر اآلن زر التشغيل ‪ Run‬الموجود في الزاوية اليس رى العلي ا من الص فحة‪ .‬إذا نف ذت الخط وات الس ابقة‬

‫بدق ة‪ ،‬س تالحظ ظه ور العب ارة ‪ !Hello Vue‬في القس م األيمن األس فل من الص فحة‪ .‬مب ارك! لق د ب نيت‬

‫تطبيق ‪ Vue.js‬األول لك‪.‬‬

‫ّ‬
‫أتوقع أنّ شيفرة ‪ HTML‬ال تي كتبناه ا س هلة وواض حة‪ ،‬حيث أنش أنا‬ ‫دعنا اآلن ّ‬
‫نحلل ما قمنا به حتى اآلن‪.‬‬

‫المعرف ‪ ،app‬ووضعنا ضمن وسمي الفتح واإلغالق له التعبير التالي‪:‬‬


‫ّ‬ ‫عنصر <‪ >div‬وأسندنا له‬

‫}} ‪{{ message‬‬

‫هذا التعبير هو األساس في ‪ ،Vue.js‬سنتحدّ ث عنه بعد قليل‪ .‬اآلن الحظ معي شيفرة ‪ JavaScript‬التالية‪:‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪message: 'Hello Vue‬‬
‫}‬
‫)}‬

‫أعتقد أنّ بداية هذه الشيفرة مألوف ة‪ ،‬حيث أ ّنن ا نعم ل على إنش اء ك ائن من الن وع ‪ Vue‬باس تخدام الكلم ة‬

‫نمرر إلى النوع ‪:Vue‬‬


‫المفتاحية ‪ .new‬الحظ معي ماذا ّ‬

‫‪14‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل ‪Vue.js‬‬

‫{‬
‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪message: 'Hello Vue‬‬
‫}‬
‫}‬

‫المقطع السابق عبارة عن كائن آخر يُ سمى بكائن الخيارات (‪ )options instance‬يحوي اإلع دادات ال تي‬
‫نريد أن تكون ضمن كائن ‪ Vue.js‬الجديد‪ .‬ففي الحقل ‪ّ ( el‬‬
‫يمثل أول ح رفين من كلم ة ‪ element‬أي العنص ر)‬

‫المس تَ هدف (في حالتن ا ه ذه ه و عنص ر ‪ div‬ال ذي‬ ‫ُنسند القيمة '‪ '#app‬وال تي ّ‬
‫تمث ل مع ّرف عنص ر ‪ُ HTML‬‬
‫أم ا الحق ل ‪ data‬فيتم إس ناد ك ائن آخ ر إلي ه يحت وي على حق ل واح د وه و ‪message‬‬
‫أنشأناه قب ل قلي ل)‪ّ ،‬‬

‫وقيمت ه '!‪ .'Hello Vue‬الح ظ أنّ الكلم ة '‪ 'message‬هي نفس ها الموج ودة ض من ُرم از ‪ HTML‬الس ابق‬

‫الموض وعة بحاض نة مزدوج ة {{ ‪ .}} message‬وهك ذا فعن د تنفي ذ البرن امج الس ابق س يعمل ‪ Vue‬على‬

‫استبدال التعبير {{ ‪ }} message‬ض من عنص ر ‪ HTML‬ال ذي يحم ل المع ّرف ‪ app‬ب النص '!‪'Hello Vue‬‬
‫وهذا ّ‬
‫كل شيء‪.‬‬

‫العملية‪.‬‬
‫ّ‬ ‫يميز بين الكائن ‪ object‬والنسخة ‪ ،instance‬إاّل أنني ال ّ‬
‫أفرق بينهما من الناحية‬ ‫رغم وجود من ّ‬
‫دوما‪.‬‬
‫لذلك تراني أستخدم "كائن" ً‬

‫حاول اآلن إجراء بعض التغييرات على النص السابق‪ ،‬وأعد التنفيذ باستخدام الزر ‪ Run‬لتشاهد التغي يرات‬

‫الجديدة على الخرج‪.‬‬

‫مجهزة مس ً‬
‫بقا‪،‬‬ ‫ّ‬ ‫مجرد عملية استبدال تعبير برسالة‬
‫ّ‬ ‫في الحقيقة إنّ ما جرى في هذا المثال يتعدّ ى عن كونه‬

‫إن ما يجري من وراء الكواليس هو عملية ربط كامل بين الحقل ‪ message‬الموجود ضمن شيفرة ‪JavaScript‬‬

‫وبين التعبير {{ ‪ }} message‬الموجود ضمن ُرماز ‪ HTML‬وسنرى كيف ذلك في الفقرة التالية‪.‬‬

‫ّ‬
‫األول‬ ‫‪ 1.3‬إضافة مزايا جديدة لتطبيقنا‬

‫حان ال وقت إلج راء بعض التحس ينات على تطبيقن ا األول‪ .‬انتق ل إلى قس م ‪ HTML‬وأض ف عنص ر إدخ ال‬

‫نصي ‪ input‬على النحو التالي‪:‬‬


‫ّ‬

‫>"‪<div id="app‬‬
‫>‪<input type='text' v-on:input="updateInfo"/‬‬
‫}} ‪{{ message‬‬
‫>‪</div‬‬

‫‪15‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل ‪Vue.js‬‬

‫لقد وضعت عنصر اإلدخال فوق التعبير {{ ‪ }} message‬مباشرة‪ .‬الش يء الجدي د هن ا ه و الس مة (أي‬

‫الموج ه ‪ directive‬وه و‪v-on :‬‬


‫ّ‬ ‫‪ )attribute‬التي تدعى ‪ v-on:input‬وهي بح د ذاته ا مكوّ ن ة من قس مين‬

‫نص ت ح دث ‪ .event listener‬وفي‬


‫الموج ه ‪ v-on‬دور ُم ِ‬
‫ّ‬ ‫الموج ه‪ .‬يلعب‬
‫ّ‬ ‫والوسيط ‪ input‬المراد تمريره إلى‬

‫حالتنا هذه فإ ّننا نرغب باإلنصات لحدث اإلدخال ‪( input‬معامل ‪ )parameter‬لعنصر اإلدخ ال النص ي‪ .‬الح ظ‬

‫تم إج راء‬
‫الموجه‪ updateInfo :‬وهي بكل بساطة اسم التابع الذي سيتم استدعاؤه في ح ال ّ‬
‫ّ‬ ‫المسندة‬
‫القيمة ُ‬
‫سنعرف هذا التابع اآلن‪.‬‬
‫ّ‬ ‫أي إدخال ضمن عنصر اإلدخال النصي من ِق َبل المستخدم‪.‬‬

‫المخصص لشيفرات ‪ JavaScript‬في الصفحة الرئيسية للموقع‬


‫ّ‬ ‫استبدل الشيفرة القديمة الموجودة بالقسم‬

‫بالشيفرة التالية‪:‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪message: 'Hello Vue‬‬
‫‪},‬‬
‫{‪methods:‬‬
‫{)‪updateInfo:function(event‬‬
‫;‪this.message = event.target.value‬‬
‫}‬
‫}‬
‫)}‬

‫الفرق الوحيد بين المقطعين الجديد والق ديم‪ ،‬أ ّنن ا أض فنا القس م ‪ methods‬ال ذي يتم في ه تعري ف جمي ع‬

‫سنعرف التابع ‪ updateInfo‬الذي تحدثنا عن ه قب ل‬


‫ّ‬ ‫التوابع (‪ )methods‬التي نرغب باستخدامها‪ .‬واضح أ ّننا‬

‫عرفنا ه ذا الت ابع على أ ّن ه دال ة ‪ّ function‬‬


‫ومررن ا إلي ه الك ائن ‪ event‬وه و ك ائن يحت وي على بيان ات‬ ‫قليل‪ّ .‬‬
‫تلقائيا بسبب كتابة المستخدم ضمن مر ّب ع النص‪ .‬العب ارة البرمجي ة الوحي دة الموج ودة‬
‫ّ‬ ‫تم توليده‬
‫الحدث الذي ّ‬
‫في هذا التابع هي‪:‬‬

‫;‪this.message = event.target.value‬‬

‫المعرف ضمن نفس ك ائن ‪ )Vue.js‬وال ذي‬


‫ّ‬ ‫على نحو غريب قلياًل ‪ ،‬ترمز الكلمة ‪ this‬هنا إلى الحقل ‪( data‬‬
‫ّ‬
‫سنوضح سبب ذل ك في فص ل الح ق‪ .‬القس م الث اني من العب ارة بس يط‪،‬‬ ‫يحتوي بدوره على الحقل ‪.message‬‬

‫وفيه نستخلص البيانات التي أدخلها المستخدم من خالل التعبير ‪.event.target.value‬‬

‫جرب تنفيذ البرنامج اآلن بنقر زر ‪ ،Run‬ثم ح اول كتاب ة أي ش يء ض من مرب ع النص‪ ،‬س تالحظ أ ّن ه س يتم‬
‫ّ‬
‫تحديث الرسالة بشكل فوري بالنص الذي تكتبه!‬

‫‪16‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل ‪Vue.js‬‬

‫‪ 1.4‬العمل عىل حاسوب محيل‬

‫محليا على حاس وبك‬


‫ًّ‬ ‫إذا لم ترغب أن تعمل على ‪ jsfiddle.net‬يمكنك العمل بالتأكيد في وضع عدم اتصال‬

‫أواًل تنزي ل ‪ Vue.js‬عن طري ق الراب ط ‪( vuejs.org/js/vue.js‬من الممكن أن يظه ر محت وى‬
‫الشخص ي‪ .‬علين ا ّ‬
‫الملف كاماًل ضمن صفحة عادية في المتصفح‪ ،‬انسخ محتويات الصفحة واحفظها ضمن جديد‪ ،‬س ّمه‪.)vue.js :‬‬

‫أم ا عن دما يص بح التط بيق قي د‬


‫في الحقيقة يمكنك استخدام هذه النسخة ألغراض التط وير البرمجي ة فق ط‪ّ ،‬‬
‫فينصح باستخدام النسخة التالية‪vuejs.org/js/vue.min.js :‬‬
‫االستخدام العملي ُ‬

‫حص لت على النس ختين الس ابقتين من الموق ع الرس مي إلط ار العم ل ‪ Vue.js‬من الص فحة التالي ة‪:‬‬
‫‪vuejs.org/v2/guide/installation.html‬‬
‫ّ‬
‫المجل د مل ف ‪ vue.js‬ال ذي ّ‬
‫نزلت ه قب ل قلي ل‪.‬‬ ‫وسمه ‪ ،vuejs-first-app‬ثم انس خ ض من ه ذا‬
‫ّ‬ ‫ّ‬
‫مجلدً ا‬ ‫أنشئ‬

‫محرر ‪ HTML‬المناسب لك‪ ،‬أنش ئ مل ف جدي د واخ تر ل ه مثال االس م ‪ .index.html‬ثم احفظ ه في‬
‫ّ‬ ‫باستخدام‬
‫ّ‬
‫المجل د الس ابق بج وار المل ف ‪ ،vue.js‬وبع د ذل ك أض ف إلي ه المحت وى الت الي ال ذي قمت بتجميع ه من‬ ‫نفس‬

‫المحسن الذي تناولناه في الفقرة السابقة‪:‬‬


‫ّ‬ ‫محتويات التطبيق‬

‫>‪<!DOCTYPE html‬‬
‫>"‪<html lang="en" dir="ltr‬‬
‫>‪<head‬‬
‫>"‪<meta charset="utf-8‬‬
‫>‪<title>Vue.js</title‬‬
‫>‪<script src="vue.js"></script‬‬
‫>‪</head‬‬

‫‪17‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل ‪Vue.js‬‬

‫>‪<body‬‬
‫>"‪<div id="app‬‬
‫>‪<input type='text' v-on:input="updateInfo"/‬‬
‫}} ‪{{ message‬‬
‫>‪</div‬‬

‫>"‪<script type="text/javascript‬‬
‫{(‪var app = new Vue‬‬
‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪message: 'Hello Vue‬‬
‫‪},‬‬
‫{‪methods:‬‬
‫{)‪updateInfo:function(event‬‬
‫;‪this.message = event.target.value‬‬
‫}‬
‫}‬
‫)}‬
‫>‪</script‬‬
‫>‪</body‬‬
‫>‪</html‬‬

‫ّ‬
‫المحلي عن طري ق وس م ‪ script‬ض من القس م ‪ head‬من‬ ‫الحظ معي أ ّننا ق د أض فنا مرجعً ا لمل ف ‪vue.js‬‬

‫المستند‪ ،‬وأضفنا شيفرة ‪ JavaScript‬المسؤولة عن تحديث خرج التطبيق ضمن القسم ‪ body‬من المستند‪.‬‬

‫منص ات مث ل ‪jsfiddle.net‬‬ ‫يمكنك متابع ة كتاب ة تطبيقات ك على ه ذا النح و‪ ،‬ول و أ ّن ني ّ‬


‫أفض ل اس تخدام ّ‬

‫و ‪ codepen.io‬على األقل في هذه المرحلة التي نسبر بها أغوار ‪.Vue.js‬‬

‫‪ 1.5‬ختام الفصل‬
‫تناولنا في هذا الفصل مقدّ مة س ريعة إلى إط ار العم ل الواع د ‪ Vue.js‬حيث ّ‬
‫تعلمن ا كي ف نب دأ م ع ‪Vue.js‬‬

‫وكيف نكتب تطبيقات بسيطة ّ‬


‫توضح لنا مدى السهولة والمرونة التي يتمتّ ع بها إط ار العم ل ه ذا‪ .‬س نتابع عملن ا‬

‫ومتدرج‪.‬‬
‫ّ‬ ‫في الفصول الالحقة لنسبر أغوار ‪ Vue.js‬بشكل سهل‬

‫‪ 1.6‬تمارين داعمة‬

‫للتدرب على المفاهيم الموجودة في هذا الفصل‪ .‬يوجد‬


‫ّ‬ ‫سأزودك في التمرينين التاليين بمسألتين بسيطتين‬

‫بعد كل تمرين حل مقترح‪ ،‬حاول أاّل تنظر إلى الحلول المقترحة قبل تقديم حلولك‪.‬‬

‫‪18‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل ‪Vue.js‬‬

‫‪ 1.6.1‬تمرين ‪1‬‬

‫الموجه ‪ v-on‬لكي يستجيب التطبيق عن دما ينق ر‬


‫ّ‬ ‫استخدم الوسيط ‪ click‬بداًل من الوسيط ‪ input‬مع‬

‫المحسن الذي تح دثنا عن ه قب ل قلي ل‪.‬‬


‫ّ‬ ‫المستخدم على عنصر اإلدخال بداًل من الكتابة داخله وذلك في التطبيق‬

‫اجعل التطبيق يعرض الرسالة "!‪ "Clicked‬عندما ينقر المستخدم‪.‬‬

‫الحل المقتـرح‬

‫الح ل في ه ذا التم رين بس يط‪ .‬اس تبدل الوس يط ‪ input‬بالوس يط ‪ clicked‬ض من ُرم از ‪ HTML‬ثم وفي‬

‫المخصص لشيفرة ‪ ،JavaScript‬استبدل بالسطر التالي‪:‬‬


‫ّ‬ ‫القسم‬

‫;‪this.message = event.target.value‬‬

‫السطر‪:‬‬

‫;'!‪this.message = 'Clicked‬‬

‫‪ 1.6.2‬تمرين ‪2‬‬

‫في هذا التمرين ستُ نشئ تطبيق آلة حاسبة بسيط يقوم بعملية حسابية واح دة وهي جم ع ع ددين‪ .‬أق ترح‬

‫الواجهة البسيطة التالية‪:‬‬

‫الم يزة في ه ذا التط بيق أ ّن ه إذا ب دأ المس تخدم بالكتاب ة في أي مر ّب ع‪ ،‬يجب أن يق وم التط بيق بإيج اد‬
‫ّ‬
‫بالتحقق من صحة إدخال المستخدم‪.‬‬ ‫المجموع بشكل فوري أثناء إدخاله األرقام‪ .‬لن نقوم في هذا التمرين‬

‫الحل المقترح‬

‫طريقة التنفيذ مش ابهة ج دّ ا للتطبيق ات ال تي تناولناه ا في ه ذا الفص ل‪ .‬سأض يف حقلين إض افيين فق ط‬

‫لتخزين قيمتي المعاملين األيمن واأليسر لعملية الجمع‪ .‬شيفرة ‪ HTML‬المقترحة هي‪:‬‬

‫>"‪<div id="app‬‬
‫>‪<input type='text' v-on:input="updateLeftOper"/‬‬
‫>‪<label>+</label‬‬
‫>‪<input type='text' v-on:input="updateRightOper"/‬‬
‫>‪<label>=</label‬‬
‫>‪<label>{{result}}</label‬‬
‫>‪</div‬‬

‫‪19‬‬
Vue.js ‫أساسيات إطار العمل‬ Vue.js ‫مدخل إىل‬

:‫ المقترحة فهي على الشكل التالي‬JavaScript ‫أما شيفرة‬

var app = new Vue({


el: '#app',
data: {
result: 0,
leftOper: 0,
rightOper: 0
},
methods:{
updateLeftOper: function(event){
this.leftOper = parseFloat(event.target.value)
this.result = this.leftOper + this.rightOper;
},
updateRightOper: function(event){
this.rightOper = parseFloat(event.target.value)
this.result = this.leftOper + this.rightOper;
}
}
})

ً
.‫الحقا‬ ‫ سنتكلم المزيد عن هذه األمور‬،‫تماما فال بأس‬
ً ‫إذا لم تستطع استيعاب الشيفرة السابقة‬

20
‫‪ .2‬التعامل مع ‪DOM‬‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫فهم قوالب ‪Vue.js‬‬ ‫•‬

‫الوصول إلى البيانات والتوابع من كائنات ‪Vue.js‬‬ ‫•‬

‫الربط مع السمات ‪Attributes‬‬ ‫•‬

‫كتابة شيفرة ‪ HTML‬خام‬ ‫•‬

‫التعامل مع األحداث ‪Events‬‬ ‫•‬

‫استخدام الربط ثنائي االتجاه‬ ‫•‬

‫نتابع عملنا في هذا الفص ل وه و الفص ل الث اني وال ذي س نتعلم في ه ه ذه الم ّرة كيفي ة الوص ول والتعامل‬

‫موجهات ‪ Vue.js‬مختلفة للوصول إلى بيان ات ك ائن ‪ Vue.js‬والتفاع ل‬


‫ّ‬ ‫ّ‬
‫سنتعلم كيف نستخدم‬ ‫مع ‪ ،DOM‬حيث‬

‫وسنتوس ع في التعام ل م ع األح داث ‪ events‬باإلض افة إلى كيفي ة اس تخدام الرب ط ثن ائي االتج اه‬
‫ّ‬ ‫معه ا‪،‬‬

‫مع العناصر‪.‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫‪ 2.1‬فهم قوالب ‪Vue.js‬‬

‫تعاملنا في الفصل السابق مع تطبيقات استخدمت مزايا بسيطة من ‪ ،Vue.js‬وإذا كنت ت ذكر أ ّنن ا ق د كتبن ا‬
‫}} واس تخدمنا ً‬
‫أيض ا‬ ‫ثم اس تخدمنا االس تبدال النص ي {{ ‪message‬‬
‫ش يفرة ‪ HTML‬بس يطة ومن ّ‬
‫موجه ‪ v-on‬لالستجابة إلى دخل المستخدم‪.‬‬
‫ّ‬

‫داخليا على شكل ق الب‬


‫ًّ‬ ‫ما يقوم به ‪ Vue.js‬من وراء الكواليس‪ ،‬هو أخذ نسخة عن شيفرة ‪ HTML‬وحفظها‬

‫‪ ،template‬بع د ذل ك يتم إج راء عملي ة تص يير (‪ )rendering‬على نس خة من الق الب الس ابق‪ ،‬باس تخدام‬

‫ثم بع د االنته اء من التص يير يتم ع رض‬


‫الموج هات وتعابير االستبدال النصي (في حال وجودها ضمن القالب)‪ّ ،‬‬
‫ّ‬

‫الخرج النهائي على المستخدم‪.‬‬

‫اآلن‪ ،‬وعندما يُ حدَ ث أي تغيير جدي د في قيم ة أي حق ل مرتب ط من ك ائن ‪ ،Vue.js‬س تُ جرى عملي ة تص يير‬

‫داخليا‪ ،‬ثم يُ ع رض الخ رج النه ائي من جديد‬


‫ًّ‬ ‫جدي دة على نس خة جدي دة من الق الب الس ابق المخ ّزن‬

‫على المستخدم‪.‬‬

‫فعلي ا في‬
‫ً‬ ‫دائم ا بين ك ائن ‪ Vue.js‬وبين ش يفرة ‪ .HTML‬وه ذا م ا رأين اه‬
‫ارتباط ا ً‬
‫ً‬ ‫أي كم ا ل و أ ّنن ا أنش أنا‬

‫التطبيقات البسيطة التي تناولناها في الفصل السابق‪.‬‬

‫من باب التذكير‪ ،‬نستخدم في هذا الكتاب الموقع ‪ jsfiddle.net‬بشكل افتراضي لتشغيل جميع‬

‫التطبيقات التي نكتبها‪ .‬ونحتاج بالطبع إلى إدراج ملف إطار العمل ‪ Vue.js‬لكي نستطيع تنفيذ هذه‬

‫التطبيقات‪ .‬إذا أردت أن تعرف كيف ذلك‪ ،‬يمكنك العودة إلى الفصل السابق‪.‬‬

‫‪ 2.2‬الوصول إىل البيانات والتوابع من كائنات ‪Vue.js‬‬

‫يمث ل ش يفرة ‪ HTML‬وث اني مقط ع ّ‬


‫يمثل‬ ‫انظ ر إلى المث ال الت الي (مث ل الع ادة‪ ،‬أول مقط ع ّ‬

‫شيفرة ‪:)JavaScript‬‬

‫>"‪<div id="app‬‬
‫}} ‪{{ title‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪title: 'Hello Vue‬‬

‫‪22‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫}‬
‫)}‬

‫عندما نستخدم االستبدال النصي {{ ‪ }} title‬كما وسبق أن فعلن ا مس ً‬


‫بقا‪ ،‬ال نس تخدم الكلم ة ‪this‬‬

‫قبل ‪ title‬كما هو واضح‪ .‬في الحقيق ة أنّ أي كلم ة ُتش ير إلى حق ل (مث ل ‪ )title‬موج ودة ض من حاض نة‬

‫مزدوجة‪ ،‬سيتم اعتبارها على أ ّنها حقل ضمن القسم ‪ data‬في كائن ‪ Vue.js‬الموافق‪ .‬اآلن عن د تنفي ذ التط بيق‬
‫ّ‬
‫متوقع‪.‬‬ ‫السابق سيؤدي إلى ظهور الجملة ‪ !Hello Vue‬كما هو‬

‫ّ‬
‫ليحق ق نفس الخ رج الس ابق‬ ‫وبالمثل ً‬
‫أيضا‪ ،‬يمكننا في الواقع استخدام تابع مثل )(‪displayMessage‬‬

‫تماما‪ ،‬وبنفس األسلوب تقري ًبا‪ .‬استخدم المثال التالي‪:‬‬


‫ً‬

‫>"‪<div id="app‬‬
‫}} )(‪{{ displayMessage‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪title: 'Hello Vue‬‬
‫‪},‬‬
‫{‪methods:‬‬
‫{)(‪displayMessage: function‬‬
‫;‪return this.title‬‬
‫}‬
‫}‬
‫)}‬

‫نة‬ ‫من الحاض‬ ‫ابع )(‪ displayMessage‬ض‬ ‫رة الت‬ ‫ذه الم‬ ‫تخدمنا ه‬ ‫ا اس‬ ‫ظ أ ّنن‬ ‫الح‬

‫ومرة أخرى لم نستخدم الكلم ة ‪ this‬قب ل اس م الت ابع‪ .‬أي ت ابع‬


‫المزدوجة {{ )(‪ّ }} displayMessage‬‬

‫يُ كتب به ذه الطريق ة س ُيعتَ بر افتراض ًّيا على أ ّن ه ت ابع موج ود ض من القس م ‪ methods‬من ك ائن ‪.Vue.js‬‬

‫ولكن ه ذا الس لوك االفتراض ي ال يس ري على ش يفرة ‪ JavaScript‬الموج ودة ض من ك ائن ‪ Vue.js‬حيث يجب‬

‫مرة أردنا فيها الوصول إلى أحد أعضاء كائن ‪.Vue.js‬‬ ‫استخدام الكلمة ‪ this‬في ّ‬
‫كل ّ‬

‫‪ 2.3‬الربط مع السمات ‪Attributes‬‬

‫ال يمكن اس تخدام تقني ة االس تبدال النص ي (باس تخدام الحاض نة المزدوج ة) إلدراج قيم ض من س مات‬

‫جيد‪ ،‬انظر معي إلى المثال التالي‪:‬‬


‫العناصر‪ .‬لفهم هذا الموضوع بشكل ّ‬

‫>"‪<div id="app‬‬

‫‪23‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫>‪<p> {{ message }} - <a href='{{link}}'>Hsoub Academy</a‬‬ ‫>‪</p‬‬


‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪message: 'Hello Vue!',‬‬
‫'‪link: 'https://academy.hsoub.com/‬‬
‫}‬
‫)}‬

‫عند تنفيذ التطبيق السابق في ‪ jsfiddle.net‬ستحصل على الخرج التالي‪:‬‬

‫ّ‬
‫متوق ع‪ ،‬ولكن إذا ج ربت النق ر على الراب ط لن ينقل ك إلى موق ع أكاديمي ة‬ ‫الحظ أ ّنه خ رج ّ‬
‫منس ق كم ا ه و‬
‫ّ‬
‫متوقع‪ ،‬في الحقيق ة س ينقلك ه ذا الراب ط إلى ص فحة ض من نفس موق ع ‪ jsfiddle.net‬وه ذه‬ ‫حسوب كما هو‬

‫الصفحة بالطبع ستكون غير موجودة‪.‬‬

‫النصي ُت ِ‬
‫عامل محتويات الحق ل ‪ link‬كنص مج ّرد‪ ،‬يمكن ك مالحظ ة‬ ‫السبب في ذلك أنّ تقنية االستبدال ّ‬

‫ر‪fiddle.jshell.net/_display/%7B%7Btitle%7D%7D :‬‬ ‫د النق‬ ‫اتج بع‬ ‫ط الن‬ ‫الراب‬

‫الموج ه‬
‫ّ‬ ‫ويتمثل في تجنّب استخدام السمة ‪ href‬بهذا الشكل‪ ،‬إ ّنما ينبغي استخدام‬
‫ّ‬ ‫الحل لهذه المشكلة بسيط‪،‬‬

‫‪ v-bind‬مع الوسيط ‪ href‬على النحو التالي‪:‬‬

‫'‪v-bind:href = 'link‬‬

‫حيث ‪ link‬هو نفسه الحقل الموجود ضمن كائن ‪ .Vue.js‬اس تبدل ب التعبير الس ابق الس مة ‪ href‬القديم ة‬

‫الموجودة ضمن شيفرة ‪ HTML‬الموجود في المثال السابق‪ ،‬بعد االستبدال سيص بح ش كل ش يفرة ‪ HTML‬على‬

‫النحو التالي‪:‬‬

‫>"‪<div id="app‬‬
‫>‪<p> {{ message }} - <a v-bind:href = 'link'>Hsoub Academy</a‬‬ ‫>‪</p‬‬
‫>‪</div‬‬

‫المرة إذا نقرت على الرابط س تنتقل‬


‫مرة أخرى‪ ،‬ستحصل على نفس الخرج‪ ،‬ولكن هذه ّ‬
‫أعد تشغيل التطبيق ّ‬
‫موج ه‬
‫ّ‬ ‫إلى موقع أكاديمية حسوب‪ً .‬‬
‫إذا كخالصة على ما سبق‪ ،‬إذا أردت أن تربط م ع الس مات فعلي ك اس تخدام‬

‫الموجه ات خالل ه ذا‬


‫ّ‬ ‫مع الوسيط المناسب بداًل من استخدام تقنية االستبدال ّ‬
‫النصي‪ .‬سنتناول ع ددً ا من ه ذه‬
‫ّ‬
‫المتوفرة‪.‬‬ ‫الموجهات‬
‫ّ‬ ‫دوما زيارة الصفحة الرسمية إلطار العمل ‪ Vue.js‬لالطالع على جميع‬
‫الكتاب‪ ،‬ويمكنك ً‬

‫‪24‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫‪ 2.4‬كتابة شيفرة ‪ HTML‬خام‬

‫نحت اج في بعض األحي ان إلى كتاب ة ش يفرة ‪ HTML‬خ ام مباش ر ًة في الص فحة‪ .‬ق د يتب ادر إلى ذهن ك أن‬

‫تأمل معي المث ال‬


‫تستخدم االستبدال النصي مع الحاضنة المزدوجة‪ ،‬ولكن لن تنفع هذه التقنية في هذه الحالة‪ّ .‬‬
‫التالي لفهم أفضل حول هذه المشكلة‪:‬‬

‫>"‪<div id="app‬‬
‫}} ‪{{ raw‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪raw: '<ul><li>First Item</li><li>Second Item</li><li>Third‬‬
‫'>‪Item</li></ul‬‬
‫}‬
‫)}‬

‫اله دف من التط بيق الس ابق ه و ع رض قائم ة غ ير مر ّتب ة عن طري ق العنص ر ‪ُ ul‬تظه ر ثالث ة عناصر‬

‫فق ط‪ First Item :‬و ‪ Second Item‬و ‪ .Third Item‬ولكن عن د التنفي ذ لن تحص ل على م ا ه و متوق ع‪،‬‬

‫ستحصل على الخرج التالي‪:‬‬

‫>‪<ul><li>First Item</li><li>Second Item</li><li>Third Item</li></ul‬‬

‫أي أ ّن ك ستحص ل على نص ع ادي دون أن يتع ّرف علي ه المتص ّفح على أ ّن ه ش يفرة ‪ .HTML‬يمكن ه ذه‬

‫المشكلة بسهولة بإجراء تعديل على شيفرة ‪ HTML‬فقط على النحو التالي‪:‬‬

‫>"‪<div id="app‬‬
‫>'‪<p v-html='raw‬‬

‫>‪</p‬‬
‫>‪</div‬‬

‫الموج ه ‪ v-html‬ال ذي يس مح في حالتن ا ه ذه باس تخدام محتوي ات‬


‫ّ‬ ‫التعديل الذي أجريته هو اس تخدام‬

‫الحق ل ‪ raw‬كش يفرة ‪ HTML‬نظامي ة وليس مج ّرد نص ع ادي (الح ظ أ ّن ني ق د تخلص ت من االس تبدال‬

‫نسقة بشكل صحيح‪.‬‬


‫النصي {{ ‪ .}} raw‬أعد تنفيذ التطبيق‪ ،‬لتحصل على قائمة ُم ّ‬
‫ّ‬

‫‪25‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫‪ 2.5‬التعامل مع األحداث (‪)Events‬‬

‫أهمية عظيمة في تطوير التطبيقات التي تتفاعل مع المستخدم‪ .‬ت دعم ‪ Vue.js‬األح داث‬
‫ّ‬ ‫لألحدث كما نعلم‬

‫جي د‪ ،‬ولقد تعاملنا في الفصل السابق مع نوعين من األحداث‪ :‬ح دث اإلدخ ال ض من مر ّب ع النص‪ ،‬وح دث‬
‫بشكل ّ‬
‫ّ‬
‫نتعلم كي ف‬ ‫النقر بزر الفأرة على عنصر ‪ .HTML‬سنتناول في هذا الفصل األحداث بشيء من التفص يل‪ ،‬حيث س‬

‫ننصت إلى أحداث الفأرة‪ ،‬باإلضافة إلى اإلنصات إلى أحداث لوحة المفاتيح‪.‬‬

‫‪ 2.5.1‬اإلنصات إىل أحداث الفأرة‬

‫نب دأ بالتعام ل م ع أح داث الف أرة‪ ،‬لتُ نعش ذاكرت ك‪ ،‬انظ ر معي إلى التط بيق البس يط الموج ود في‬

‫الفصل السابق‪:‬‬

‫>"‪<div id="app‬‬
‫>‪<input type='text' v-on:input="updateInfo"/‬‬
‫}} ‪{{ message‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪message: 'Hello Vue‬‬
‫‪},‬‬
‫{‪methods:‬‬
‫{)‪updateInfo:function(event‬‬
‫;‪this.message = event.target.value‬‬
‫}‬
‫}‬
‫)}‬

‫الموجه ‪،v-on‬‬
‫ّ‬ ‫سبق وأن ذكرنا في الفصل السابق أ ّنه يتم اإلنصات إلى أي حدث ضمن عنصر ما باستخدام‬

‫بتغير نوع الح دث الم راد اإلنص ات ل ه‪ .‬بع د تحدي د ن وع الوس يط‬
‫الموجه ّ‬
‫ّ‬ ‫مرر لهذا‬
‫الم ّ‬
‫حيث يتم تغيير الوسيط ُ‬
‫للموجه ‪ v-on‬يتم تحديد التابع ال ذي سيس تجيب (س يعالج) ه ذا الح دث‪ ،‬وه و عب ارة عن ت ابع‬
‫ّ‬ ‫المراد تمريره‬

‫ضمن القسم ‪ methods‬ضمن كائن ‪ Vue.js‬يتم استدعاؤه عند وقوع الحدث‪.‬‬

‫بالنسبة للتابع المعالج للحدث (التابع ‪ updateInfo‬في المث ال الس ابق) س يتم تولي د ك ائن يحت وي على‬

‫مهم ة حول الحدث الذي وقع‪ ،‬ويتم تمرير هذا الكائن بشكل تلقائي إلى التابع المعالج للحدث‪.‬‬
‫معلومات ّ‬

‫‪26‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫كيفية للتابع المعالج للحدث‪ .‬انظر معي‬


‫ّ‬ ‫على العموم‪ ،‬يمكن االستغناء عن هذا السلوك التلقائي‪ ،‬وتمرير قيمة‬

‫إلى المثال التالي‪:‬‬

‫>'‪<div id='app‬‬
‫>‪<button v-on:click="increase(2)">Increase!</button‬‬
‫>‪<p>{{counter}}</p‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪counter: 0‬‬
‫‪},‬‬
‫{‪methods:‬‬
‫{)‪increase: function(value‬‬
‫;‪this.counter += value‬‬
‫}‬
‫}‬
‫)}‬

‫الشيفرة السابقة مألوفة‪ ،‬مع مالحظتين جديدتين‪ .‬األولى أ ّننا لم نكتفي بكتابة اس م الت ابع المع الج للح دث‬

‫الموجه ‪ v-on:click‬فحسب‪ ،‬إ ّنما قد ّ‬


‫مررنا القيمة ‪ 2‬كوسيط لهذا التابع على الشكل‪increase(2) :‬‬ ‫ّ‬ ‫ضمن‬

‫(الح ظ ش يفرة ‪ .)HTML‬بالمقاب ل‪ ،‬إذا ت أملت ش يفرة ‪ JavaScript‬ض من تعري ف الت ابع ‪ increase‬في‬

‫كمتغي ر يحم ل قيم ة عددي ة وليس كك ائن يحم ل‬


‫ّ‬ ‫القس م ‪ ،methods‬فس تالحظ أ ّنن ا نعام ل الوس يط ‪value‬‬

‫لعملية اس تدعاء الت ابع المع الج‪.‬‬


‫ّ‬ ‫معلومات حول الحدث الذي وقع‪ .‬أي أ ّننا قد استطعنا تغيير السلوك االفتراضي‬

‫المتغي ر ‪ counter‬بمق دار القيم ة‬


‫ّ‬ ‫بالنسبة للمث ال الس ابق‪ ،‬فكم ا ه و واض ح‪ ،‬يعم ل التط بيق على زي ادة قيم ة‬

‫مرة يتم فيها نقر الزر‪.‬‬


‫‪( value‬في مثالنا السابق ستكون تساوي ‪ )2‬في كل ّ‬

‫كيفي ة بنفس ال وقت‪ .‬ت دعم‬


‫ّ‬ ‫في بعض الحاالت قد نحتاج إلى تمرير كائن الحدث باإلضافة إلى تمرير قيم ة‬

‫‪ Vue.js‬هذا األمر ببساطة من خالل تمرير الكلمة المحجوزة ‪ $event‬إلى الت ابع المع الج باإلض افة إلى القيم ة‬

‫الكيفي ة الم راد تمريره ا‪ .‬إذا أردن ا تط بيق ذل ك على المث ال األخ ير فسيص بح تعري ف ال زر ‪ button‬على‬

‫النحو التالي‪:‬‬

‫>‪<button v-on:click="increase(2, $event)">Increase!</button‬‬

‫وبالنسبة لتعريف التابع المعالج ضمن القسم ‪ methods‬فسيصبح على النحو التالي‪:‬‬

‫‪27‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫{)‪increase: function(value, event‬‬


‫;‪this.counter += value‬‬
‫}‬

‫مجرد إضافة وسيط آخر‪.‬‬


‫ّ‬ ‫أي‬

‫‪ 2.5.2‬التعديل عىل كيفية االستجابة لألحداث‬

‫كيفية االستجابة لألح داث‪ ،‬فربم ا نحت اج في وقت م ا إلى إيق اف‬
‫ّ‬ ‫نحتاج في بعض األحيان أن ُنعدّ ل على‬

‫االستجابة لحدث ما ألحد العناصر دو ًنا عن العناصر األخرى في ‪ .HTML‬لكي أضرب لك مث ااًل جمياًل ح ول ه ذا‬
‫ّ‬
‫مؤش ر الف أرة‬ ‫األمر‪ ،‬اسمح لي أواًل أن أقدّ م لك حدث حركة الفأرة ‪ .mousemove‬يُ ّ‬
‫ول د ه ذا الح دث عن د م رور‬

‫الموجه ‪ .v-on‬انظر إلى المثال البسيط التالي‪:‬‬


‫ّ‬ ‫ّ‬
‫متوقع مع‬ ‫فوق عنصر ما‪ ،‬ويتم استخدامه كما هو‬

‫>"‪<div id="app‬‬
‫>'‪<p v-on:mousemove='updateCoordinates‬‬
‫)}}‪Mouse cursor at: ({{x}}, {{y‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪x:0,‬‬
‫‪y:0‬‬
‫‪},‬‬
‫{‪methods:‬‬
‫{)‪updateCoordinates:function(event‬‬
‫;‪this.x = event.clientX‬‬
‫;‪this.y = event.clientY‬‬
‫}‬
‫}‬
‫)}‬

‫حرك الف أرة ف وق العنص ر الوحي د الظ اهر أمام ك‪ .‬ستحص ل على‬
‫ثم ّ‬
‫جرب تنفيذ التطبيق البسيط السابق‪ّ ،‬‬
‫ّ‬

‫خرج شبيه بما يلي‪:‬‬

‫ابع‬ ‫ه الت‬ ‫ندنا إلي‬ ‫ه ‪ v-on:mousemove‬حيث أس‬ ‫الموج‬


‫ّ‬ ‫تخدام‬ ‫و اس‬ ‫اه‬ ‫د هن‬ ‫الجدي‬

‫المعالج ‪ updateCoordinates‬المع َّرف بطبيع ة الح ال ض من القس م ‪ methods‬في ك ائن ‪ .Vue.js‬الح ظ‬

‫‪28‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫ّ‬
‫لمؤش ر الف أرة (الفاص لة ‪ x‬وال تراتيب ‪ )y‬ض من‬ ‫معي ً‬
‫أيض ا كي ف نحص ل على اإلح داثيات الحالي ة‬

‫التابع ‪:updateCoordinates‬‬

‫;‪this.x = event.clientX‬‬
‫;‪this.y = event.clientY‬‬

‫اآلن إذا أردنا أن ننشئ منطقة "ميتة" (ضمن عنصر ‪ span‬مثال) ضمن عنصر ‪ p‬ال ذي يع رض اإلح داثيات‪،‬‬
‫ّ‬
‫مؤش ر الف أرة ف وق ه ذه المنطق ة إلى تولي د الح دث ‪ ،mousemove‬فينبغي علين ا عن دها‬ ‫بحيث ال يؤدّ ي مرور‬

‫التعديل على الحدث ‪ mousemove‬كما يلي (سألوّ ن التعديالت اإلضافية باألخضر)‪:‬‬

‫>"‪<div id="app‬‬
‫>'‪<p v-on:mousemove='updateCoordinates‬‬
‫‪Mouse cursor at: ({{x}}, {{y}}) -‬‬
‫>‪<span v-on:mousemove='uncoveredArea'>Uncovered Area</span‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪x:0,‬‬
‫‪y:0‬‬
‫‪},‬‬
‫{‪methods:‬‬
‫{)‪updateCoordinates:function(event‬‬
‫;‪this.x = event.clientX‬‬
‫;‪this.y = event.clientY‬‬
‫‪},‬‬
‫{)‪uncoveredArea: function(event‬‬
‫;)(‪event.stopPropagation‬‬
‫}‬
‫}‬
‫)}‬

‫الموجه ‪ v-on:mousemove‬إلى العنصر ‪ span‬وأسندت المعالج ‪ uncoveredArea‬له‪.‬‬


‫ّ‬ ‫أضفت‬

‫بالنس بة للت ابع ‪ uncoveredArea‬فق د أج ريت تع ديل على الح دث من خالل اس تدعاء‬

‫التابع )(‪ stopPropagation‬من الكائن ‪ .event‬المعنى الحرفي لهذا الت ابع ه و "إيق اف االنتش ار" أي أ ّنن ا‬
‫ّ‬
‫مؤشر الفأرة فوق العنصر ‪.span‬‬ ‫سنمنع االستجابة لهذا الحدث عندما يمر‬

‫ّ‬
‫مؤشر الفأرة ف وق عنص ر ‪ .span‬إذا‬ ‫جرب تنفيذ التطبيق السابق‪ ،‬والحظ التغيير الذي سيحدث عندما يمر‬
‫ّ‬

‫أردت اإلحساس بالفرق‪ ،‬يمكنك أن تحذف التعليمة )(‪ event.stopPropagation‬ثم أعد تنفي ذ التط بيق‬

‫‪29‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫ّ‬
‫مؤش ر الف أرة ف وق عنص ر ‪ span‬ه ذه الم رة‪.‬‬ ‫تتغير عن دما يم ر‬
‫ّ‬ ‫م ّرة أخ رى‪ ،‬ل ترى كي ف أنّ اإلح داثيات س‬

‫يمكن استخدام صيغة أبسط للتعديل على األحداث‪ ،‬فمن الممكن حذف الت ابع ‪ uncoveredArea‬بالكام ل من‬

‫بالموجه على النحو التالي‪:‬‬


‫ّ‬ ‫قسم ‪ ،methods‬واالكتفاء بالقسم الخاص‬

‫''=‪v-on:mousemove.stop‬‬

‫أي أ ّننا قد استغنينا عن الشيفرة الالزمة إليقاف انتش ار الح دث ‪ .mousemove‬نس مي ‪ .stop‬هن ا بمع ِّدل‬

‫الحدث (‪ )event modifiers‬وهناك عدّ ة معدّ الت أحداث مفيدة سنستعرض بعضها في هذا الكتاب‪.‬‬

‫‪ 2.5.3‬اإلنصات إىل أحداث لوحة المفاتيح‬

‫يمكننا أحيا ًنا أن نحتاج إلى اإلنصات ً‬


‫أيضا إلى األحداث الناشئة من لوحة المفاتيح‪ .‬واألس لوب المتب ع هن ا‪،‬‬

‫يشبه إلى حدّ كبير ما كنّا نفعله م ع أح داث الف أرة‪ .‬إذا أردن ا مثاًل اإلنص ات إلى ح دث تحري ر مفت اح من لوح ة‬

‫للموجه ‪ v-on‬على النحو التالي‪:‬‬


‫ّ‬ ‫المفاتيح يمكن أن نستخدم الوسيط ‪keyup‬‬

‫'‪v-on:keyup='methodName‬‬

‫حيث ‪ methodName‬ه و اس م الت ابع المع الج للح دث ‪ keyup‬وال ذي يجب أن يُ َ‬


‫وض ع ض من القس م‬
‫‪ .methods‬دعنا اآلن ّ‬
‫نوظف ذلك في مثال بسيط‪:‬‬

‫>"‪<div id="app‬‬
‫>‪<input type='text' v-on:keyup='keyIsUp' /‬‬
‫>‪<p‬‬
‫}}‪{{message‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'' ‪message:‬‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫{ )‪keyIsUp: function(event‬‬
‫;‪this.message = event.target.value‬‬
‫}‬
‫}‬
‫)}‬

‫تم تحري ر مفت اح من لوح ة المف اتيح‪،‬‬ ‫يعم ل ه ذا التط بيق البس يط على تح ديث الحق ل ‪ّ message‬‬
‫كلم ا ّ‬
‫وبالتالي سيؤدّ ي ذلك إلى تحديث محتويات عنصر ‪ p‬ضمن الواجهة‪.‬‬

‫‪30‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫ولكن دعنا نتساءل‪ ،‬ماذا لو أردنا أن يستجيب المعالج ‪ّ keyIsUp‬‬


‫كلما ُحرِّ ر مفتاح المسافة (‪ )space‬فقط‪،‬‬

‫أي مفت اح يُ ح رِّ ره المس تخدم‪ .‬الج واب ببس اطة‪ ،‬ه و في اس تخدام مع دّ ل الح دث ‪ .space‬بع د‬
‫وليس عن د ّ‬
‫‪ .keyup‬أضف فقط الكلمة ‪ .space‬إلى ‪ keyup‬إلى المثال السابق‪ .‬أي على النحو التالي‪:‬‬

‫'‪v-on:keyup.space = 'keyIsUp‬‬

‫أع د تنفي ذ التط بيق ل ترى أنّ محتوي ات عنص ر ‪ p‬أص بحت ال ُتح دَّ ث إاّل بع د تحري ر المفت اح ‪.space‬‬

‫تمث ل جمي ع المف اتيح على لوح ة المف اتيح‪ ،‬فهن اك مثاًل ‪ enter‬و ‪tab‬‬
‫يوجد بالطبع العديد من المعدّ الت التي ّ‬

‫و ‪ up‬لمفتاح السهم العلوي‪ ،‬و ‪ down‬لمفتاح السهم السفلي وهكذا‪.‬‬

‫ّ‬
‫يتمث ل أبس طها في إرس ال المحت وى ال ذي أدخل ه‬ ‫لمع دّ الت أح داث لوح ة المف اتيح الكث ير من الفوائ د‪،‬‬

‫المستخدم بمجرد ضغطه للمفت اح ‪ ،Enter‬أو إرس ال البيان ات مباش ر ًة بينم ا يكتبه ا المس تخدم للحص ول على‬

‫مقترحات أثناء عملية الكتابة (كما يفعل مح ّرك البحث غوغ ل أثن اء كتاب ة المس تخدم للمف ردات الم راد البحث‬

‫عنها)‪ .‬وغيرها الكثير من االستخدامات‪.‬‬

‫‪ 2.6‬استخدام الربط ثنائي االتجاه‬

‫في معظم األمثلة السابقة عم دنا إلى اس تخدام رب ط باتج اه واح د‪ ،‬من الش يفرة إلى عنص ر ‪ .HTML‬وفي‬

‫بعض الحاالت استطعنا أن نعكس ه ذا األم ر‪ .‬أي اس تطعنا تع ديل قيم ة الحق ل عن طري ق االنص ات إلى ح دث‬

‫مخصص لهذه الغاية‪ .‬ولكن توجد طريقة مباشرة وس هلة إليج اد‬
‫ّ‬ ‫اإلدخال ‪ v-on:input‬وبالتالي معالج حدث‬

‫الموج ه ‪ v-model‬وب دون الحاج ة إلى مع الج ح دث‪،‬‬


‫ّ‬ ‫ربط ثنائي االتج اه فعلي في ‪ Vue.js‬وذل ك باس تخدام‬

‫كمال في المثال التالي‪:‬‬

‫>"‪<div id="app‬‬
‫>‪<input type='text' v-model='name'/‬‬
‫}} ‪{{ name‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫'!‪name: 'Hello Vue‬‬
‫}‬
‫)}‬

‫‪31‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫التطبيق السابق بس يط‪ ،‬وه و يعم ل على إج راء رب ط ثن ائي االتج اه بين الحق ل ‪ name‬وبين عنص ر مر ّب ع‬

‫تغي ر ألح دهما‬


‫النص‪ ،‬أي س يكون هن اك ارتب اط آني بين الحق ل ‪ name‬وبين عنص ر مر ّب ع النص‪ ،‬ف إذا ح دث ّ‬
‫نفذ التطبيق السابق وسترى مباشر ًة الخرج التالي‪:‬‬
‫سينعكس مباشر ًة على اآلخر‪ّ .‬‬

‫تلقائي ا بقيم ة الحق ل ‪ ،name‬وبالمث ل إذا ح اولت اآلن‬


‫ً‬ ‫تمت تعبئت ه‬
‫الحظ كيف أنّ محتوى مر ّب ع النص ق د ّ‬
‫كتابة أي شيء ضمن مربّع النص سيتم تعديل قيمة الحقل ‪ name‬فو ًرا ً‬
‫وفقا له‪ ،‬وبالتالي س ُيعدّ ل محت وى النص‬

‫الموجود في الطرف األيمن بسبب وجود االستبدال النصي {{‪.}}name‬‬

‫‪ 2.7‬ختام الفصل‬

‫كيفي ة التعام ل م ع ‪ ،DOM‬حيث تح دثنا عن الق والب‪ ،‬وكي ف نص ل إلى البيان ات‬
‫ّ‬ ‫ّ‬
‫تعلمن ا في ه ذا الفص ل‬
‫ّ‬
‫وتعلمن ا كيفي ة الرب ط‬ ‫كيفية الربط مع السمات‪ ،‬والتعامل م ع األح داث‪،‬‬
‫ّ‬ ‫والتوابع بشيء من التفصيل‪ ،‬كما ّ‬
‫تعلمنا‬

‫ثنائي االتجاه الذي يسمح لنا بمزامنة البيانات باالتجاهين‪.‬‬

‫‪ 2.8‬تمارين داعمة‬

‫‪ 2.8.1‬تمرين ‪1‬‬

‫يُ طلب في هذا التمرين تطوير تطبيق اآللة الحاسبة البسيط الذي بنين اه في الفص ل الس ابق‪ .‬بحيث يس مح‬

‫التطبيق الجديد بإجراء العمليات الحسابية األرب ع ب داًل من عملي ة الجم ع الوحي دة ال تي ك ان ي دعمها التط بيق‬

‫السابق‪ .‬أقترح الواجهة التالية للتطبيق‪:‬‬

‫الح ظ أ ّن ني ق د اس تخدمت عنص ر ‪ ،select‬يمكن ك اس تخدام أي طريق ة أخ رى الختي ار العملي ات‬

‫الحسابية األربع‪.‬‬

‫وجد التطبيق الناتج النهائي إذا حدث أحد األمرين التاليين‪:‬‬


‫من الضروري أن يُ ِ‬

‫تغيير قيمة أحد المعاملين على طرفي العملية الحسابية‪.‬‬ ‫•‬

‫‪32‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع ‪DOM‬‬

‫تغيير العملية الحسابية عن طريق القائمة المنسدلة‪.‬‬ ‫•‬

‫أرجو أن يتمكن التطبيق من تمييز حالة القسمة على صفر‪ ،‬وإظهار رسالة مناسبة للمستخدم‪.‬‬

‫‪ 2.8.2‬تمرين ‪2‬‬

‫يُ طلب في هذا التمرين إنشاء تطبيق يقدّ م للمستخدم مقترحات ّ‬


‫نصية بينما يكتب المس تخدم ض من مر ّب ع‬

‫نصية ضمن الش يفرة‪ .‬وس أعتبر أنّ‬


‫نص‪ .‬سنحاكي عملية االتصال مع خادوم بعيد عن طريق استخدام مصفوفة ّ‬
‫المستخدم يحاول أن يُ دخل اسم دولة عربية‪ ،‬فتظهر قائمة المقترحات باألسفل بينم ا تتم عملي ة اإلدخ ال‪ .‬كم ا‬

‫في الشكل التالي‪:‬‬

‫ستحتاج بالطبع إلى التعامل مع أحداث لوحة المفاتيح‪ ،‬ولكي يكون األمر أكثر سهولة بالنسبة إليك‪ ،‬يمكن ك‬

‫فترض أن تكون قادمة من الخادوم‪:‬‬


‫استخدام المصفوفة الجاهزة التالية كمصدر للبيانات التي يُ َ‬

‫[‬
‫‪',‬السعودية'‬
‫‪',‬البحرين'‬
‫‪',‬مصر'‬
‫‪',‬السودان'‬
‫‪',‬ليبيا'‬
‫‪',‬الجزائر'‬
‫‪',‬المغرب'‬
‫‪',‬تونس'‬
‫‪',‬موريتانيا'‬
‫‪',‬العراق'‬
‫‪',‬سوريا'‬
‫‪',‬لبنان'‬
‫‪',‬قطر'‬
‫‪',‬اإلمارات'‬
‫‪',‬الصومال'‬
‫‪',‬جزر القمر'‬
‫‪',‬الكويت'‬
‫ُمان'‬
‫‪',‬سلطنة ع‬
‫‪',‬األردن'‬
‫‪',‬اليمن'‬
‫'فلسطين'‬
‫]‬

‫‪33‬‬
‫‪ .3‬الموجهات الشـرطية والتكرارية‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫كتابة شفرات ‪ JavaScript‬مباشر ًة ضمن القوالب‬ ‫•‬

‫التصيير الشرطي باستخدام ‪ v-if‬و ‪ v-else‬و ‪v-else-if‬‬ ‫•‬

‫الفرق بين ‪ v-if‬و ‪v-show‬‬ ‫•‬

‫تصيير القوائم باستخدام ‪v-for‬‬ ‫•‬

‫المرور على خاصيات كائن‬ ‫•‬

‫ّ‬
‫سنتعلم فيه كيفية كتابة شفرات ‪ JavaScript‬مباشر ًة ضمن‬ ‫نتابع عملنا في هذا الفصل وهو الفصل الثالث‪،‬‬

‫الق والب دون الحاج ة إلى االس تعانة ب التوابع كم ا كنَّا نفع ل من قب ل‪ ،‬باإلض افة إلى اس تخدام‬

‫التص يير (‪ )rendering‬الش رطي باس تخدام ‪ v-if‬وأخوات ه ‪ v-else‬و ‪ .v-else-if‬كم ا س نتحدّ ث عن‬

‫الشكلية‪ ،‬ونختم الفصل بالح ديث عن التك رار‬


‫ّ‬ ‫الفرق بين ‪ v-if‬و ‪ v-show‬اللذان لهما نفس التأثير من الناحية‬

‫باستخدام ‪.v-for‬‬

‫هيا بنا لنسبر أغوار ‪!Vue.js‬‬


‫َّ‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫‪ 3.1‬كتابة شفرات ‪ JavaScript‬مبارشة ضمن القوالب‬

‫يمكننا في الكثير من األحيان أن نكتب شيفرة ‪ JavaScript‬مباشر ًة ضمن القوالب‪ ،‬دون الحاجة إلى إنش اء‬

‫تابع خاص ووضعه في قسم ‪ .methods‬نلجأ إلى ه ذا األس لوب‪ ،‬في ح ال ك انت الش يفرة قص يرة وال تحت وي‬

‫على الكثير من التعقيد‪ ،‬باإلضافة إلى ضرورة أن يكون الناتج النهائي للشيفرة عبارة عن تعب ير (‪.)expression‬‬

‫انظر معي إلى المثال التالي‪:‬‬

‫>"‪<div id="app‬‬
‫‪<span>Current temperature:</span><input type='text' v-‬‬
‫>‪model='temperature' /‬‬
‫>‪<p‬‬
‫'‪{{temperature > 35 ? 'Hot': temperature < 20 ? 'Cold' : 'Moderate‬‬
‫}}‬
‫>‪</p‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪temperature: 30‬‬
‫}‬
‫)}‬

‫يعمل التطبيق السابق على عرض مر ّب ع إدخال للمستخدم‪ ،‬حيث يعرض عليه إدخال درجة الحرارة الحالية‪،‬‬

‫آنيا أثناء الكتابة على النحو التالي‪:‬‬


‫قيم دخل المستخدم ًّ‬
‫ثم يُ ّ‬

‫إذا كانت درجة الحرارة أكبر من ‪ ،35‬سيعرض التطبيق الرسالة ‪Hot‬‬ ‫•‬

‫إذا كانت درجة الحرارة أصغر من ‪ ،20‬سيعرض التطبيق الرسالة ‪Cold‬‬ ‫•‬

‫ّ‬
‫يتحق ق الش رطان الس ابقان س يعرض التط بيق الرس الة ‪ Moderate‬أي معت دل‪.‬‬ ‫إذا لم‬ ‫•‬

‫النصي‪:‬‬
‫إذا نظرت إلى قسم ‪ HTML‬فستجد الشيفرة التالية ضمن االستبدال ّ‬

‫'‪temperature > 35 ? 'Hot': temperature < 20 ? 'Cold' : 'Moderate‬‬

‫شيفرة ‪ JavaScript‬هذه عبارة عن تعبير بسيط يستخدم العامل (‪ )operator‬الثالثي ‪ ?:‬بشكل مت داخل‬

‫الختبار الحاالت الثالث السابقة‪.‬‬

‫‪35‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫الحظ معي أ ّننا قد استخدمنا هن ا الرب ط ثن ائي االتج اه '‪ 'v-model‬لرب ط قيم ة الحق ل ‪temperature‬‬

‫مباشرة بدخل المستخدم (يمكنك العودة للفصل السابق لمراجعة هذا الموضوع)‪ .‬الح ظ ً‬
‫أيض ا أنّ ك ائن ‪Vue.js‬‬

‫في قسم ‪ JavaScript‬بسيط للغاية وال يحتوي على أية توابع‪.‬‬

‫‪ 3.2‬التصييـر الشـرطي باستخدام ‪ v-if‬و ‪ v-else‬و ‪v-else-if‬‬


‫تحقق (أو عدم ّ‬
‫تحقق) شرط م ا‪ .‬يمكن‬ ‫نحتاج في الكثير من األحيان إلى إخفاء جزء من الصفحة في حال ّ‬
‫الموج ه ‪ v-if‬وأخوات ه‪ .‬لنس تخدم أواًل‬
‫ّ‬ ‫تحقي ق ه ذا األم ر بس هولة في ‪ Vue.js‬من خالل اس تخدام‬

‫الموجه ‪ v-if‬لوحده‪:‬‬
‫ّ‬

‫>"‪<div id="app‬‬
‫>"‪<button v-on:click="show = !show‬‬
‫!‪Click this‬‬
‫>‪</button‬‬
‫>"‪<p v-if="show‬‬
‫!‪Welcome to Hsoub Academy‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪show: true‬‬
‫}‬
‫)}‬

‫التطبيق السابق بسيط‪ ،‬تتكوّ ن الواجهة من زر عادي ورسالة‪ .‬عند نقر هذا الزر بشكل متك ّرر‪ ،‬تظه ر الرس الة‬
‫في األسفل أو تختفي ً‬
‫وفقا لذلك‪.‬‬

‫المعرف ضمن قسم ‪ ،data‬والذي أسندت إلي ه القيم ة '‪ 'true‬افتراض ًيا‪ .‬بالنس بة‬
‫ّ‬ ‫نالحظ أواًل الحقل ‪show‬‬

‫الموجه ‪ v-on:click‬وكيف أسندت إليه شيفرة ‪ JavaScript‬مباشر ًة‪:‬‬


‫ّ‬ ‫لشيفرة ‪ HTML‬الحظ‬

‫‪v-on:click = "show‬‬ ‫"‪= !show‬‬

‫أيض ا‪ .‬م ا تفعل ه‬ ‫ً‬


‫مسبقا على إدراج تابع مع الج للح دث‪ ،‬ولكن من الممكن كتاب ة ش يفرة ‪ً JavaScript‬‬ ‫اعتدنا‬
‫هذه الش يفرة بس يط ج دًّ ا‪ ،‬فهي تعم ل على تغي ير قيم ة ‪ show‬بين ‪ true‬و ‪ false‬بش كل متن اوب ّ‬
‫كلم ا نق ر‬

‫المستخدم على الزر‪.‬‬

‫‪36‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫الموجه ‪ v-if‬ض من عنص ر ‪ p‬ال ذي س يحتوي على النص ‪.Welcome to Hsoub Academy‬‬
‫ّ‬ ‫وضعت‬

‫الموجه قيمة الحقل ‪ .show‬فعندما تكون قيمة ‪ show‬تساوي ‪ true‬س يظهر عنص ر ‪ p‬م ع الرس الة‬
‫ّ‬ ‫أسندت لهذا‬

‫المطلوبة للمستخدم‪ ،‬وإاّل س يختفي عنص ر ‪ p‬بش كل كام ل‪ ،‬ليس من أم ام المس تخدم فحس ب‪ ،‬وإ ّنم ا من كامل‬

‫بنية ‪ ،DOM‬وهذا النقطة من الضروري االنتباه إليها‪.‬‬

‫الموجه ‪ v-else‬نفس ال دور ال ذي‬


‫ّ‬ ‫الموجه ‪ .v-if‬يلعب‬
‫ّ‬ ‫من الممكن ً‬
‫أيضا استخدام الموجه ‪ v-else‬بعد‬

‫تلعبه عب ارة ‪ else‬في ‪ ،JavaScript‬انظ ر إلى المث ال البس يط الت الي وه و تع ديل بس يط عن المث ال الس ابق‪،‬‬

‫سيكون التعديل على شيفرة ‪ HTML‬فقط‪:‬‬

‫>"‪<div id="app‬‬
‫>"‪<button v-on:click="show = !show‬‬
‫!‪Click this‬‬
‫>‪</button‬‬
‫>"‪<p v-if="show‬‬
‫!‪Welcome in Hsoub Academy‬‬
‫>‪</p‬‬
‫>‪<p v-else‬‬
‫!‪Welcome in Vue.js‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫اإلضافة الوحيدة هي‪:‬‬

‫>‪<p v-else‬‬
‫!‪Welcome in Vue.js‬‬
‫>‪</p‬‬

‫الموج ه ‪ v-else‬وهك ذا فعن دما ينق ر المس تخدم ال زر بش كل متك رر‪ ،‬س تتناوب‬
‫ّ‬ ‫اس تخدمت‬

‫اوي ‪ ture‬يظهر‬ ‫ة ‪ show‬تس‬ ‫ون قيم‬ ‫دما تك‬ ‫ذلك بين ‪ true‬و ‪ false‬عن‬ ‫ال‬ ‫ة ‪ show‬تبعً‬ ‫قيم‬

‫أما عندما تكون ‪ false‬يظهر النص !‪.Welcome in Vue.js‬‬


‫النص !‪ّ Welcome to Hsoub Academy‬‬

‫الموج ه ‪ v-else-if‬ال ذي‬


‫ّ‬ ‫ويمكن ً‬
‫أيض ا اعتب ا ًرا من اإلص دار ‪ 2.1‬للمكتب ة ‪ Vue.js‬اس تخدام‬

‫يقابل ‪ else if‬في لغة ‪. JavaScript‬‬

‫الموجهات الش رطية الس ابقة م ع عناص ر ‪ HTML‬أخ رى مث ل ‪ div‬و ‪.template‬‬


‫ّ‬ ‫يمكن استخدام كل من‬

‫الميزة في استخدام العنصر ‪ template‬هو أ ّنه ال يظهر في ‪ DOM‬عند عرض الصفحة‪ .‬فه و يلعب دور حاوي ة‬

‫ال ُت َّ‬
‫صير عند عرض الصفحة‪ .‬سأعدّ ل المثال السابق ضمن قسم ‪ HTML‬فقط‪:‬‬

‫>"‪<div id="app‬‬
‫>"‪<button v-on:click="show = !show‬‬

‫‪37‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫!‪Click this‬‬
‫>‪</button‬‬
‫>"‪<template v-if="show‬‬
‫>‪<h2‬‬
‫!‪Welcome to Hsoub Academy‬‬
‫>‪</h2‬‬
‫>‪<p‬‬
‫‪Here you can learn Vue.js.‬‬
‫>‪</p‬‬
‫>‪</template‬‬
‫>‪</div‬‬

‫الموج ه ‪ v-if‬ض من العنص ر‬


‫ّ‬ ‫وضعت العنصرين ‪ h1‬و ‪ p‬ض من العنص ر ‪ .template‬ه ذه الم رة‪ ،‬وض عت‬

‫‪ template‬وهكذا س نتمكّن من إخف اء كام ل العنص ر ‪ template‬م ع العناص ر الموج ودة ض منه‪ ،‬أو إظه اره‬

‫مر معنا‪.‬‬
‫بعناصره‪ ،‬وذلك بحسب قيمة الحقل ‪ show‬كما ّ‬

‫تماما‪ ،‬ولكن في ه ذه الحال ة يظه ر‬


‫يمكن استخدام العنصر ‪ div‬مكان العنصر ‪ template‬بنفس األسلوب ً‬
‫ّ‬
‫محق ًق ا (قيم ة الحق ل ‪ show‬تس اوي ‪ )true‬في حين ال‬ ‫العنصر ‪ div‬ضمن ‪ DOM‬في حال كان شرط اإلظه ار‬
‫ّ‬
‫محق ًقا‪ ،‬فاألمر يعود إليك في تحدي د العنص ر‬ ‫يظهر العنصر ‪ template‬في ‪ DOM‬حتى ولو كان شرط اإلظهار‬

‫المناسب الحتياجاتك‪.‬‬

‫‪ 3.3‬الفرق بني ‪ v-if‬و ‪v-show‬‬


‫الموج ه ‪ ،v-if‬في إخف اء أو إظه ار عنص ر ً‬
‫وفق ا لش رط‬ ‫ّ‬ ‫ً‬
‫عوضا عن‬ ‫الموجه ‪v-show‬‬
‫ّ‬ ‫من الممكن استخدام‬

‫معين كما وسبق أن رأينا قبل قليل‪.‬‬


‫ّ‬

‫الموجه ‪ v-if‬يؤدي إلى إزال ة العنص ر بش كل كام ل‬


‫ّ‬ ‫الموجهين السابقين‪ ،‬في أنّ‬
‫ّ‬ ‫يكمن الفرق األساسي بين‬

‫الموج ه ‪ v-show‬فيعم ل على إخف اء العنص ر فق ط‪ ،‬دون إزالت ه‬


‫ّ‬ ‫من ‪ DOM‬كما لو أ ّنه غير موجود باألصل‪ّ .‬‬
‫أم ا‬

‫بالكامل من ‪.DOM‬‬

‫يمكن أن تلحظ الفرق بينهما بسهولة من خالل المثال البسيط التالي‪:‬‬

‫>"‪<div id="app‬‬
‫>"‪<button v-on:click="show = !show‬‬
‫!‪Click this‬‬
‫>‪</button‬‬
‫>"‪<p v-if="show‬‬
‫!‪Welcome to Hsoub Academy‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫‪38‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫الموجه ‪ .v-if‬بالنسبة لقسم ‪ JavaScript‬فهو نفسه كما في األمثلة السابقة‪.‬‬


‫ّ‬ ‫الحظ أ ّنني استخدمت اآلن‬

‫اهرا‪ .‬انق ر علي ه ب زر الف أرة األيمن واخ تر‬


‫عن دما يك ون النص !‪ Welcome to Hsoub Academy‬ظ ً‬
‫‪ Inspect‬من القائمة المنبثقة إذا كنت تستخدم ‪ Google Chrome‬أو اختر ‪ Inspect Element‬إذا كنت‬

‫تستخدم ‪ .FireFox‬سترى شيًئ ا شبيهً ا بما يلي‪:‬‬

‫انقر اآلن على الزر ‪ ،‬سيختفي النص ب الطبع‪ .‬انظ ر إلى ناف ذة العناص ر م ّرة أخ رى‪ ،‬لتج د أنّ عنص ر ‪ p‬ال ذي‬
‫ّ‬
‫وحل مكانه رمز تعليق (‪ )comment‬كما في الش كل الت الي‬ ‫تماما من المستند‪،‬‬
‫يحتوي النص السابق قد اختفى ً‬
‫(الحظ المستطيل األحمر)‪:‬‬

‫الموج ه ‪ .v-show‬ه ذه الم ّرة لن‬


‫ّ‬ ‫بالموج ه ‪v-if‬‬
‫ّ‬ ‫ك ّرر اآلن نفس التجرب ة الس ابقة‪ ،‬ولكن بع د أن تس تبدل‬

‫كليا من ‪ ،DOM‬إ ّنم ا ستس تخدم ‪ Vue.js‬تنس يق ‪ CSS‬واس مه ‪ display‬إلخفائ ه من أم ام‬


‫يختفي العنص ر ‪ًّ p‬‬
‫مرة أخرى)‪:‬‬
‫تماما من ‪ .DOM‬انظر الشكل التالي (الحظ المستطيل األحمر ّ‬
‫المستخدم دون إزالته ً‬

‫‪39‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫‪ 3.4‬تصييـر القوائم باستخدام ‪v-for‬‬

‫من الممكن توليد قائمة من العناصر اعتبا ًرا من بيانات موجودة على شكل مصفوفة وذلك بشكل تلقائي من‬

‫عموم ا‪ .‬ليكن‬
‫ً‬ ‫الموجه ‪ ،v-for‬وهو يشبه إلى حدّ كبير حلقة ‪ for‬التكراريّ ة في لغ ات البرمج ة‬
‫ّ‬ ‫خالل استخدام‬

‫لدينا مص فوفة اس مها ‪ fruits‬تحت وي على العناص ر التالي ة‪ Apple :‬و ‪ Banana‬و ‪ Orange‬و ‪ .Kiwi‬نري د‬

‫إظهار هذه البيانات ضمن قائمة غير مر ّتبة‪ .‬يمكن استخدام البرنامج التالي‪:‬‬

‫>"‪<div id="app‬‬
‫>‪<ul‬‬
‫>‪<li v-for='fruit in fruits'>{{fruit}}</li‬‬
‫>‪</ul‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: "#app",‬‬
‫{ ‪data:‬‬
‫]'‪fruits:['Apple', 'Banana', 'Orange', 'Kiwi‬‬
‫}‬
‫)}‬

‫الموجه ‪ v-for‬ضمن العنصر ‪ li‬على النحو التالي‪:‬‬


‫ّ‬ ‫وضعنا‬

‫'‪v-for='fruit in fruits‬‬

‫المعرف ضمن‬
‫ّ‬ ‫أما ‪ fruits‬فهو نفس الحقل‬
‫المتغير ‪ fruit‬هنا كيفي‪ ،‬يمكنك استخدام أي اسم آخر‪ّ .‬‬
‫ّ‬ ‫اسم‬

‫القسم ‪ data‬في كائن ‪ .Vue.js‬النتيجة ستكون على النحو التالي‪:‬‬

‫ل ‪ fruits‬من خدمة‬ ‫من الحق‬ ‫ودة ض‬ ‫ات الموج‬ ‫ل على البيان‬ ‫الطبع أن نحص‬ ‫من الممكن ب‬

‫ويب (‪ )Web Service‬مثاًل ‪.‬‬

‫إذا أردت أن تحص ل على دلي ل (‪ )index‬العنص ر ً‬


‫أيض ا‪ ،‬يمكن ذل ك بس هولة ب إجراء التع ديل الت الي على‬

‫الموجه ‪:v-for‬‬
‫ّ‬

‫‪40‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫>‪<li v-for='(fruit, i) in fruits'>{{fruit}} - ({{i}})</li‬‬

‫اإلضافتين الجديدتين‪ )fruit, i( :‬و ({{‪ .)}}i‬الحظ كيف وض عنا المتغ ير ‪ fruit‬أواًل ثم المتغ ير‬

‫ال ذي س يع ّبر عن ال دليل بع ده‪ ،‬واالثن ان ض من قوس ين ع اديين (‪ )fruit, i‬وال ترتيب به ذه الص ورة مهم‬

‫بالطبع‪ .‬عند التنفيذ سيظهر دليل كل عنصر بج واره بين قوس ين ع اديين‪ ،‬م ع المالحظ ة أنّ ال ترقيم س يبدأ من‬

‫ّ‬
‫المتمث ل‬ ‫صفرا‪ ،‬وهذا بديهي بالطبع‪ .‬في الواقع ي ذكرني ه ذا األس لوب‬
‫ً‬ ‫األول سيكون‬
‫الصفر‪ ،‬أي أنّ دليل العنصر ّ‬
‫في استخالص الدليل‪ ،‬بأسلوب لغة البرمجة بايثون في ذلك‪.‬‬

‫‪ 3.5‬المرور عىل خصائص كائن‬


‫الموجه ‪ً v-for‬‬
‫أيض ا في الم رور على خص ائص ك ائن م ا‪ .‬س يكون ه ذا الك ائن عب ارة عن‬ ‫ّ‬ ‫يمكن استخدام‬

‫خاصية مع اس مها‪ .‬ليكن ل دينا‬


‫ّ‬ ‫كائن ‪ JavaScript‬عادي سنعمل باستخدام هذا الموجه على استخالص قيمة كل‬

‫الكائن التالي‪:‬‬

‫}‪customer: {name: 'Ahmad', age:30, items: 3‬‬

‫يتألف هذا الكائن من ثالث خصائص‪ name :‬و ‪ age‬و ‪.items‬‬

‫سنمر اآلن على قيم هذه الخصائص على النحو التالي‪:‬‬

‫>'‪<div v-for='value in customer‬‬


‫>‪<p‬‬
‫}}‪{{value‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: "#app",‬‬
‫{ ‪data:‬‬
‫}‪customer: {name: 'Ahmad', age:30, items: 3‬‬
‫}‬
‫)}‬

‫يمكنك بالطبع استخدام أي عنصر من ‪ HTML‬لعرض هذه القيم‪ .‬ستظهر كل قيمة ضمن عنصر ‪ p‬خاص بها‪.‬‬

‫إذا أردن ا تط وير المث ال البس يط الس ابق‪ ،‬بحيث يص بح من الممكن أن يك ون ل دينا مجموع ة من‬

‫نمر على هذه المجموع ة‪ ،‬م ع ع رض خص ائص ك ل ك ائن (زب ون) منه ا‪ ،‬فإ ّنن ا‬
‫الزبائن (‪ )customers‬ونريد أن ّ‬

‫موجهي ‪ v-for‬متداخلين على النحو التالي‪:‬‬


‫ّ‬ ‫سنستخدم‬

‫‪41‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫>"‪<div id="app‬‬
‫>'‪<div v-for='customer in customers‬‬
‫>'‪<div v-for='value in customer‬‬
‫>‪<p‬‬
‫}}‪{{value‬‬
‫>‪</p‬‬
‫>‪</div‬‬
‫>‪<hr‬‬
‫>‪</div‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: "#app",‬‬
‫{ ‪data:‬‬
‫{[ ‪customers:‬‬
‫‪name: 'Ahmad',‬‬
‫‪age: 30,‬‬
‫‪items: 3‬‬
‫‪},‬‬
‫{‬
‫‪name: 'Saeed',‬‬
‫‪age: 28,‬‬
‫‪items: 13‬‬
‫‪},‬‬
‫{‬
‫‪name: 'Majd',‬‬
‫‪age: 21,‬‬
‫‪items: 12‬‬
‫}‬
‫]‬
‫}‬
‫)}‬

‫الحظ كيف أجريت تعدياًل على اسم الحقل ‪ customer‬ليصبح ‪ customers‬ضمن القسم ‪ data‬من كائن‬

‫وجهي ‪ v-for‬المت داخلين‪ ،‬يم ّر الخ ارجي على ك ل عنص ر من عناص ر المص فوفة‬ ‫‪ .Vue.js‬الح ظ ً‬
‫أيض ا م ّ‬
‫‪ ،customers‬في حين يم ّر ال داخلي على جمي ع قيم الخص ائص الموج ودة ض من ك ائن ‪ customer‬مح دّ د‪.‬‬

‫ستحصل عند التنفيذ على خرج شبيه بما يلي‪:‬‬

‫‪42‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫الخاصية دون اسمها‪ .‬يمكن بأس لوب مش ابه لعملي ة الحص ول على‬
‫ّ‬ ‫في المثالين األخيرين حصلنا على قيمة‬

‫الخاصية باإلضافة إلى قيمته ا‪ .‬أج ِر التع ديل البس يط الت الي‬
‫ّ‬ ‫دليل العنصر ضمن المصفوفة‪ ،‬الحصول على اسم‬

‫يمر على الخصائص ليصبح على النحو التالي‪:‬‬


‫موجه ‪ v-for‬الداخلي الذي ّ‬
‫ّ‬ ‫على‬

‫>'‪<div v-for='(value, key) in customer‬‬

‫والترتيب هنا مهم ً‬


‫أيضا‪.‬‬

‫الح ظ أن ‪ Vue.js‬ت راقب بعض تواب ع المص فوفة ‪ customers‬ال تي تعاملن ا معه ا في المث ال الس ابق‬

‫الموجه ‪ .v-for‬هذه التوابع التي سأسردها اآلن‪ُ ،‬ت ّ‬


‫غير في الحالة الداخلية للمصفوفة‪ ،‬وبالتالي تعمل‬ ‫ّ‬ ‫باستخدام‬

‫‪ Vue.js‬إلى إحداث تغيير فوري على الخرج يتوافق مع التغيير الذي حدث‪ .‬هذه التوابع هي‪:‬‬

‫)(‪push‬‬
‫)(‪pop‬‬
‫)(‪shift‬‬
‫)(‪unshift‬‬
‫)(‪splice‬‬
‫)(‪sort‬‬
‫)(‪reverse‬‬

‫الموجه ‪ v-on:click‬على النحو التالي‪:‬‬


‫ّ‬ ‫جرب أن تضيف إلى المثال األخير زر‪ ،‬وأسند إليه‬
‫ّ‬

‫‪<button v-on:click='customers.push({name: "Hasan", age:24,‬‬


‫>')}‪items:15‬‬
‫‪Add new customer‬‬
‫>‪</button‬‬

‫‪43‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫الموجهات الشـرطية والتكرارية‬

‫تلقائيا رغم‬
‫ًّ‬ ‫بعد تنفيذ البرنامج ونقر هذا الزر‪ ،‬ستالحظ أنّ الزبون الذي اسمه ‪ Hasan‬قد ُأ ضيف إلى الخ رج‬

‫أ ّننا أضفناه إلى المصفوفة ‪ customers‬فقط‪.‬‬

‫‪ 3.6‬ختام الفصل‬

‫الموجه ات الش رطية مث ل ‪ v-if‬و ‪ v-else‬و ‪v-else-if‬‬


‫ّ‬ ‫كيفي ة التعام ل م ع‬
‫ّ‬ ‫ّ‬
‫تعلمن ا في ه ذا الفص ل‬
‫ّ‬
‫وتعلمنا م تى‬ ‫ميزنا بين الموجهين ‪ v-if‬و ‪v-show‬‬
‫وكيفية استخدامها في تطبيقات ‪ .Vue.js‬كما ّ‬
‫ّ‬ ‫و ‪v-show‬‬

‫الموج ه ‪ v-for‬ال ذي يلعب نفس ال دور ال ذي تلعب ه‬


‫ّ‬ ‫تعرفنا ً‬
‫أيض ا في ه ذا الفص ل على‬ ‫نستخدم كل منهما‪ .‬كما ّ‬
‫الحلقات التكرارية في لغات البرمجة بشكل ع ام‪ ،‬واس تخدمناه للم رور على عناص ر المص فوفات‪ ،‬باإلض افة إلى‬

‫المرور على خصائص كائن محدّ د‪.‬‬

‫‪ 3.7‬تمارين داعمة‬

‫‪ 3.7.1‬تمرين ‪1‬‬

‫أجر تعدياًل على التطبيق الزبائن ‪ customers‬السابق‪ .‬في هذه المرة يجب أن تظه ر أس ماء الزب ائن فق ط‪،‬‬
‫ضمن عنصر القائمة المنسدلة ‪ select‬وذلك باستخدام ‪ً v-for‬‬
‫أيضا‪.‬‬

‫‪ 3.7.2‬تمرين ‪2‬‬

‫يُ طلب في هذا التمرين‪ ،‬تط وير التط بيق الموج ود في التم رين ‪ 2‬من الفص ل الس ابق‪ .‬ه ذه الم رة س تجعل‬

‫الموجه ‪ v-for‬بداًل من األسلوب الذي استخدمته هناك‪.‬‬


‫ّ‬ ‫الشيفرة أسهل بكثير بعد استخدام‬

‫‪44‬‬
‫‪ .4‬المزيد حول كائن ‪Vue.js‬‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫إنشاء أكثر من كائن ‪Vue.js‬‬ ‫•‬

‫الوصول إلى عناصر ‪ HTML‬مباشر ًة باستخدام ‪$refs‬‬ ‫•‬

‫الخصائص المحسوبة ضمن كائن ‪Vue.js‬‬ ‫•‬

‫الخصائص المراقبة ضمن كائن ‪Vue.js‬‬ ‫•‬

‫تثبيت قالب جديد باستخدام )(‪$mount‬‬ ‫•‬

‫المستَ هدَ ف‬
‫فصل القالب عن عنصر ‪ُ HTML‬‬ ‫•‬

‫ما هو المكوّ ن (‪)Component‬؟‬ ‫•‬

‫نتعرف على كيفي ة إنش اء أك ثر من ك ائن ‪Vue.js‬‬


‫ّ‬ ‫ّ‬
‫سنتعلم في هذا الفصل المزيد عن كائن ‪ ،Vue.js‬حيث س‬

‫وسنتعرف على الخص ائص المحس وبة‬


‫ّ‬ ‫وكيف نصل إلى خصائص كل كائن من خالل شيفرة ‪ JavaScript‬عادية‪.‬‬
‫والخصائص المراقبة‪ ،‬وسنعود ً‬
‫أيضا إلى القوالب‪ ،‬وكيف نث ّبت قالب م ا إلى عنص ر ‪ HTML‬بش كل ب رمجي‪ ،‬كم ا‬

‫سنتعرف على المكوّ نات والحاجة إليها‪ ،‬وسنبني تطبيق بسيط للغاية يعتمد على مكوّ ن يعم ل على التحوي ل من‬
‫ّ‬

‫واحدة الكيلو غرام إلى واحد الباوند‪.‬‬


‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫‪ 4.1‬إنشاء أكثـر من كائن ‪Vue.js‬‬

‫من الممكن إنشاء أكثر من كائن ‪ Vue.js‬ضمن نفس التطبيق‪ .‬حيث يمكن ربط كل كائن بعنصر ‪ div‬كما ك ّن ا‬
‫ً‬
‫مسبق ا‪ .‬يمكن لكل كائن أن يعمل بصورة مستقلة عن الكائن اآلخر‪ ،‬وفي ذلك فائدة كبيرة‪ ،‬إذ يمكننا تقس يم‬ ‫نفعل‬
‫واجهة المستخدم الرئيسية إلى أجزاء متعدّ دة‪ ،‬كل منها ّ‬
‫ينفذ وظيفة محدّ دة‪ ،‬بدون أن تتداخل مع بعضها‪.‬‬

‫مهما في‬
‫في الحقيقة تقودنا هذه الميزة المهم ة إلى بن اء المكوّ ن ات (‪ .)Components‬يلعب المك وّ ن دو ًرا ًّ‬
‫البرمجية وفي عملية إعادة االستخدام للشيفرة من ِق َبلك‪ ،‬أو من ِق َبل أي شخص آخر‪ .‬سنتحدّ ث‬
‫ّ‬ ‫تنظيم الشيفرة‬

‫عن المكونات في ‪ Vue.js‬بشكل مبدئي في هذا الفصل‪.‬‬

‫بسيطا ّ‬
‫يوضح كيفية إنشاء أكثر من كائن واحد بنفس الوقت‪:‬‬ ‫ً‬ ‫لنأخذ اآلن مثااًل‬

‫>"‪<div id="app1‬‬
‫}}‪{{title‬‬
‫>‪</div‬‬

‫>"‪<div id="app2‬‬
‫}}‪{{title‬‬
‫>‪</div‬‬

‫{(‪var instance1 = new Vue‬‬


‫‪el:'#app1',‬‬
‫{ ‪data:‬‬
‫'‪title: 'From first instance‬‬
‫}‬
‫)}‬

‫{(‪var instance2 = new Vue‬‬


‫‪el:'#app2',‬‬
‫{ ‪data:‬‬
‫'‪title: 'From second instance‬‬
‫}‬
‫)}‬

‫لألول المع ّرف ‪ app1‬وللث اني المع ّرف‬


‫عرفت عنصري ‪ div‬أسندت ّ‬ ‫ً‬
‫بداية ّ‬ ‫الشيفرة السابقة سهلة ومباشرة‪.‬‬

‫‪ .app2‬كل من العنصرين السابقين ال يحتوي إال على االستبدال النصي {{‪.}}title‬‬

‫بالنس بة لش يفرة ‪ JavaScript‬ف األمر بس يط ً‬


‫أيض ا‪ .‬فق د ع ّرفت ك ائني ‪ Vue.js‬وأس ندت ك ل منهم ا إلى‬

‫متغيرين منفصلين ‪ instance1‬و ‪ .instance2‬الكائنين متشابهين من حيث الشكل العام ولكنهم ا يختلف ان‬

‫للخاصية ‪ title‬لكل منهما كما هو واضح‪.‬‬


‫ّ‬ ‫النصية‬
‫ّ‬ ‫بالقيمة‬

‫‪46‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫عند تنفيذ التطبيق السابق ستحصل على شكل شبيه بما يلي‪:‬‬

‫من الواضح ظهور نصين مختلفين من كائنين مختلفين‪ ،‬رغم أ ّنه لك ل منهم ا نفس اس م الخاص ية ‪.title‬‬

‫يمكن ب الطبع وج ود أك ثر من ك ائنين‪ ،‬ويمكن بديه ًة أن يك ون لك ل ك ائن عت اده الخ اص من الخص ائص‬

‫والدوال وغيرها‪.‬‬

‫مهمة لكائن ات ‪ ،Vue.js‬وهي إمكاني ة الوص ول ألي ك ائن من ش يفرة ‪ JavaScript‬عادي ة‪.‬‬ ‫توجد ً‬
‫أيضا ميزة ّ‬

‫انظر إلى المثال المعدّ ل عن المثال السابق (التعديل سيكون على شيفرة ‪ JavaScript‬فقط)‪:‬‬

‫{(‪var instance1 = new Vue‬‬


‫‪el:'#app1',‬‬
‫{ ‪data:‬‬
‫'‪title: 'From first instance‬‬
‫}‬
‫)}‬

‫{(‪var instance2 = new Vue‬‬


‫‪el:'#app2',‬‬
‫{ ‪data:‬‬
‫'‪title: 'From second instance‬‬
‫}‬
‫)}‬

‫;'!‪instance1.$data.title='This text from outside‬‬

‫التعديل الوحيد الذي حدث هو في السطر األخير‪ .‬انظر كيف كتبت شيفرة ‪ JavaScript‬عادية للوصول إلى‬

‫الخاصية ‪ title‬للكائن ‪ .instance1‬بعد التنفيذ ستحصل على شكل شبيه بما يلي‪:‬‬

‫األول‪ .‬استطعت تعديل النص من خ ارج الك ائن‪ .‬وهن اك أم ر آخ ر‪ّ ،‬‬
‫لعل ك ق د‬ ‫الحظ معي كيف أصبح السطر ّ‬
‫انتبهت إلى الخاص ّية ‪ .$data‬في الحقيق ة ه و ك ائن ّ‬
‫يول ده ‪ Vue.js‬بش كل تلق ائي لكي يس مح للم برمجين‬

‫بالوصول إلى الخصائص الداخلية للقسم ‪.data‬‬

‫هذا دليل واضح على أنّ ‪ Vue.js‬مندمجة بشكل ممتاز مع ‪ JavaScript‬وليست بدياًل عنها‪ ،‬إ ّنما ّ‬
‫مكمل ة له ا‪.‬‬
‫ّ‬
‫المولدة بهذه الطريقة‪.‬‬ ‫سنشاهد عددً ا من هذه الكائنات والدوال‬

‫‪47‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫‪ 4.2‬الوصول إىل عنارص ‪ HTML‬مبارشة‬

‫توج د أك ثر من طريق ة للوص ول إلى عناص ر ‪ HTML‬ض من ‪ .DOM‬تض يف ‪ Vue.js‬طريق ة أخ رى له ا‬

‫باستخدام المفت اح ‪ .ref‬يس مح ه ذا المفت اح للم برمج بالوص ول إلى أي عنص ر ‪ HTML‬بس هولة كب يرة‪ .‬يمكن‬

‫استخدام هذا األسلوب بالشكل التالي‪:‬‬

‫>"‪<div id="app‬‬
‫>'‪<button v-on:click='changeText' ref='testButton‬‬
‫‪Old Text‬‬
‫>‪</button‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪methods:‬‬
‫{ )(‪changeText: function‬‬
‫;'!‪this.$refs.testButton.innerText = 'New Text‬‬
‫}‬
‫}‬
‫)}‬

‫الجديد هنا هو وضع الكلمة ‪ ref‬كما لو أ ّنها س مة ض من العنص ر ‪ button‬وإس ناد القيم ة ‪testButton‬‬

‫له ا‪ .‬الكلم ة ‪ ref‬ليس ت س مة قياس ية في ‪ HTML‬بالتأكي د‪ ،‬إنم ا هي كلم ة تابع ة لـ ‪ .Vue.js‬انظ ر اآلن إلى‬

‫شيفرة ‪ JavaScript‬وتحديدً ا ضمن التابع ‪ changeText‬ستالحظ السطر التالي‪:‬‬

‫;'!‪this.$refs.testButton.innerText = 'New Text‬‬

‫تلقائيا‪ .‬انتبه إلى وجود ح رف ‪s‬‬


‫ًّ‬ ‫الخاصية الجديدة هنا هي ‪ $refs‬وهي عبارة عن كائن ‪ّ JavaScript‬‬
‫مولد‬

‫الخاص بالجمع آخر كلمة ‪ ،$refs‬أل ّن ه من الممكن اس تخدام أك ثر من كلم ة ‪ ref‬م ع عناص ر ‪ HTML‬مختلف ة‪.‬‬

‫عينّاه ضمن الكلمة ‪ key‬في ‪ .HTML‬في‬


‫الخاصية ‪ testButton‬وهي تحمل نفس االسم الذي ّ‬
‫ّ‬ ‫انظر ً‬
‫أيضا إلى‬
‫الحقيق ة إنّ ‪ testButton‬عب ارة عن ك ائن ‪ً JavaScript‬‬
‫أيض ا ّ‬
‫يمث ل عنص ر في ‪ ،HTML‬ول ذلك اس تطعنا‬

‫استخدام الخاصية ‪ innerText‬منه‪.‬‬

‫عند تنفيذ التطبيق السابق‪ .‬ستحصل على زر وحيد يحمل النص ‪ .Old Text‬بعد نقر الزر‪ ،‬ستحص ل على‬

‫النص الجديد !‪ New Text‬كما هو متوقع‪.‬‬

‫نصح بتعديل خصائص عناصر ‪ HTML‬بهذا األسلوب‪ ،‬أنص ح باس تخدام ه ذا األس لوب فق ط‬
‫في الواقع ال يُ َ‬
‫لقراءة خصائص عناصر ‪ HTML‬في حال الحاجة‪.‬‬

‫‪48‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫‪ 4.3‬الخصائص المحسوبة في ‪Vue.js‬‬

‫سنتحدّ ث في هذه الفقرة عن مفه وم الخص ائص أو الخاص يات المحس وبة (‪،)Computed Properties‬‬

‫والحاجة إليها عند بناء تطبيقات باستخدام ‪ .Vue.js‬تشبه بنية الخصائص المحس وبة البني ة الخاص ة ب التوابع‪،‬‬

‫الخاصية المحسوبة يجب أن ُترجع قيمة ما‪.‬‬


‫ّ‬ ‫ّ‬
‫يتمثل في أنّ‬ ‫في أ ّنهما عبارة عن دوال‪ ،‬مع فرق بسيط‬

‫ُت ّ‬
‫عرف الخصائص المحسوبة ضمن قسم جديد اسمه ‪ computed‬يُ وضع على نفس مس تو القس مين ‪data‬‬

‫و ‪ methods‬أي على الشكل التالي‪:‬‬

‫{(‪var app = new Vue‬‬


‫‪el: "#app",‬‬
‫{ ‪data:‬‬
‫‪...‬‬
‫‪},‬‬
‫{ ‪computed:‬‬
‫‪...‬‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫‪...‬‬
‫}‬
‫)}‬

‫بيا‪ ،‬حيث تس مح بتنفي ذ الش يفرة‬


‫يرا نس ً‬
‫مهم ا عن دما يك ون التط بيق كب ً‬
‫تلعب الخصائص المحس وبة دو ًرا ّ‬
‫محسن (‪ ،)Optimized‬فهي تتجنّب المعالجة غير الضرورية ألج زاء من الش يفرة في ح ال لم‬
‫َّ‬ ‫البرمجية بشكل‬

‫يقيم درج ات الح رارة برس ائل‬


‫يطرأ تغيير ما عليها‪ .‬لفهم الخص ائص المحس وبة بش كل أفض ل‪ ،‬س نأخذ تط بيق ّ‬
‫نصية بسيطة لنفهم الحاجة إلى الخصائص المحسوبة‪:‬‬
‫ّ‬

‫>"‪<div id="app‬‬
‫‪<span>Current temperature:</span><input type='text' v-‬‬
‫>‪model='temperature' /‬‬
‫>‪<span> - Clouds:</span‬‬
‫>'‪<select v-model='clouds‬‬
‫>‪<option>Yes</option‬‬
‫>‪<option>No</option‬‬
‫>‪</select‬‬
‫>‪<p‬‬
‫)‪<b>Result:</b> (Computed) {{evaluation_computed}} | (Method‬‬
‫}})(‪{{evaluation_method‬‬
‫>‪</p‬‬
‫>‪<p‬‬
‫}}'‪{{clouds == 'Yes'? 'With some clouds.':'And the sky is clear.‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫‪49‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪temperature: 30,‬‬
‫'‪clouds: 'Yes‬‬
‫‪},‬‬
‫{ ‪computed:‬‬
‫{ )(‪evaluation_computed: function‬‬
‫;)'‪console.log('Computed‬‬
‫? ‪return this.temperature > 35 ? 'Hot' : this.temperature < 20‬‬
‫;'‪'Cold' : 'Moderate‬‬
‫}‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫{ )(‪evaluation_method: function‬‬
‫;)'‪console.log('Method‬‬
‫? ‪return this.temperature > 35 ? 'Hot' : this.temperature < 20‬‬
‫;'‪'Cold' : 'Moderate‬‬
‫}‬
‫}‬
‫)}‬

‫بعد تنفيذ البرنامج السابق ستحصل على خرج شبيه بما يلي‪:‬‬

‫عرفت مربع نص‪ ،‬باإلض افة إلى عنص ر قائم ة منس دلة‬
‫بالنسبة لقسم ‪ HTML‬أعتقد أنّ األمور واضحة‪ ،‬فقد ّ‬

‫يحمل القيمتين ‪ Yes‬و ‪ No‬لإلش ارة إلى وج ود غي وم في الس ماء أم ال‪ .‬ك ل من العنص رين الس ابقين مرب وطين‬

‫الموجه ‪ v-model‬بالحقلين الموافقين من القسم ‪ data‬في كائن ‪.Vue.js‬‬


‫ّ‬ ‫ربطا ثنائي االتجاه باستخدام‬
‫ً‬

‫األول مسؤول عن عرض تقييم الوض ع الح الي للج و‬


‫عرفت بعد ذلك عنصري ‪ p‬لعرض النتائج‪ .‬عنصر ‪ّ p‬‬
‫ثم ّ‬
‫ّ‬

‫فيما إذا كان حا ًرا أم معتداًل أم باردً ا بحسب قيمة الحقل ‪ ،temperature‬والحظ هنا أ ّنني أعرض التق ييم من‬

‫الخاصية المحس وبة ‪ evaluation_computed‬والت ابع )(‪ evaluation_method‬على الت والي لغ رض‬

‫فيس تخدم إلخب ار المس تخدم في ح ال وج ود بعض الغي وم أم أنّ‬


‫أما عنص ر ‪ p‬الث اني ُ‬ ‫ّ‬
‫سأوضحه لك بعد قليل‪ّ .‬‬
‫السماء صافية بحسب القيمة التي اختارها المستخدم من عنصر القائم ة المنس دلة الس ابق‪ .‬ال أدري إن كنت ق د‬

‫المعرف ض من‬
‫ّ‬ ‫انتبهت إلى أ ّن ني ال أضع قوسي االستدعاء بعد اسم الخاصية المحسوبة على عكس التابع العادي‬

‫القسم ‪.methods‬‬

‫‪50‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫واء في الخاص ية المحس وبة‬ ‫ً‬


‫بداية أنّ الشيفرة البرمجية الموج ودة س ً‬ ‫بالنسبة لشيفرة ‪ ،JavaScript‬الحظ‬

‫أيضا أ ّنني ق د‬
‫تماما‪ .‬والحظ ً‬
‫‪ evaluation_computed‬أم في التابع )(‪ evaluation_method‬متطابقة ً‬
‫وضعت في كل منهما تعليمة الكتابة إلى الطرفية (‪ )console‬الخاصة بأدوات المطوّ ر ض من المتص ّفح ل نراقب‬
‫كيف ستُ َّ‬
‫نفذ كل منهما‪.‬‬

‫اعرض أدوات المط وّ ر في المتص ّفح اآلن (اض غط المفت اح ‪ ،)F12‬ثم ّ‬


‫غي ر درج ة الح رارة في مر ّب ع النص‪.‬‬

‫ستالحظ ظهور الكلمتين ‪ Computed‬و ‪ Method‬على التوالي‪ ،‬مما يشير إلى أنّ التنفيذ قد دخل إلى الخاصية‬

‫تمر الكلمت ان الس ابقتان‬


‫المحس وبة ‪ evaluation_computed‬والت ابع )(‪ .evaluation_method‬ستس ّ‬
‫بالظهور على هذا النمط‪ّ ،‬‬
‫كلما أجريت أي تغيير في درجة الحرارة‪.‬‬

‫األمر المثير اآلن‪ ،‬هو أ ّنه إذا ّ‬


‫غيرت االختيار الحالي ضمن عنصر القائم ة المنس دلة‪ ،‬س تجد الكلم ة ‪Method‬‬

‫قد ظهرت وحدها ضمن الطرفية‪ ،‬ولن تظهر الكلمة ‪ Computed‬مما يشير إلى أنّ الش يفرة البرمجي ة الموج ودة‬
‫ضمن الخاصية المحسوبة لم ُت َّ‬
‫نفذ!‬

‫تتغي ر فليس هن اك حاج ة لتنفي ذ‬ ‫ً‬


‫إذا‪ ،‬فالخاصية المحسوبة ذكية بما يكفي لكي تدرك أنّ درجة الح رارة لم ّ‬
‫مرة‪ .‬بعبارة أخرى‪ ،‬تدرك الخاصية المحسوبة أنّ الشيفرة البرمجية الموج ودة‬
‫الخاصة بها كل ّ‬
‫ّ‬ ‫الشيفرة البرمجية‬

‫تم تغييره ا بش كل أو ب آخر‪ ،‬فتختص ر عملي ة التنفي ذ وتع رض التق ييم الس ابق‬
‫ضمنها ال تحتوي على خصائص ّ‬

‫لدرجة الحرارة طالما أ ّنها لم ّ‬


‫تتغير!‬

‫الناتج التالي نتج معي بع د تغي ير حال ة الغي وم ع دّ ة م رات دون تغي ير قيم ة درج ة الح رارة س وى الم ّرة‬

‫األولى فقط‪:‬‬

‫‪ 4.4‬الخصائص المراقبة ضمن كائن ‪Vue.js‬‬

‫رغم أنّ الخصائص المحسوبة كافية في معظم األحيان إاّل أ ّن ه هن اك بعض الح االت ال تي يك ون اس تخدام‬
‫َ‬
‫المراقب ة (‪ )Watched Properties‬تش به إلى ح دّ كب ير الخص ائص‬ ‫الخصائص المراقبة أفض ل‪ ،‬والخص ائص‬

‫وتتميز عنها بميزتين أساسيتين‪:‬‬


‫ّ‬ ‫المحسوبة‪،‬‬

‫‪51‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫‪ .1‬يمكن تنفيذ شيفرة برمجية غير متزامنة ضمنها‪ ،‬بمعنى أ ّنه يمكن تنفيذ مهام بشكل متوازي مع التطبيق‬

‫األساسي كاالتصال بخادوم بعيد مثاًل ‪.‬‬

‫َ‬
‫المراقبة كما كنّا نفعل مع الخصائص المحسوبة‪.‬‬ ‫‪ .2‬ال نحتاج إلى إرجاع قيمة من الخصائص‬

‫ُتع ّرف الخص ائص المحس وبة ض من قس م جدي د اس مه ‪ watch‬يُ وض ع على نفس مس توى ب اقي األقس ام‬

‫األخرى مثل ‪ data‬و ‪ methods‬و ‪ computed‬أي على الشكل التالي‪:‬‬

‫{(‪var app = new Vue‬‬


‫‪el: "#app",‬‬
‫{ ‪data:‬‬
‫‪...‬‬
‫‪},‬‬
‫{ ‪computed:‬‬
‫‪...‬‬
‫‪},‬‬
‫{ ‪watch:‬‬
‫‪...‬‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫‪...‬‬
‫}‬
‫)}‬

‫بسيطا‪:‬‬
‫ً‬ ‫لنأخذ مثااًل‬

‫>"‪<div id="app‬‬
‫>‪<input type="text" v-model='content' /‬‬
‫>‪<p‬‬
‫‪The input has changed: {{counter}} times.‬‬
‫>‪</p‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪data:‬‬
‫‪content: '',‬‬
‫‪counter: 0‬‬
‫‪},‬‬
‫{ ‪watch:‬‬
‫{ )(‪content: function‬‬
‫;‪tmp = this‬‬
‫{ )(‪setTimeout(function‬‬
‫;‪tmp.counter++‬‬
‫;)‪}, 2000‬‬
‫}‬

‫‪52‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫}‬
‫)}‬

‫اس تخدمت في المث ال الس ابق مرب ع إدخ ال نص ي يس مح للمس تخدم بكتاب ة م ا ي رغب‪ .‬عن دما يكتب‬

‫مرات التعديل التي أجراها المستخدم‪،‬‬


‫المستخدم أي شيء ضمن مربّع النص‪ ،‬سيعمل التطبيق على إحصاء عدد ّ‬

‫ويُ ظهر ذلك في األسفل ضمن رسالة مناسبة‪.‬‬

‫استخدمت الربط ثنائي االتجاه باستخدام ‪ v-model‬للربط مع الحقل ‪( content‬راجع الفص ل الث اني)‪.‬‬

‫سنراقب الحقل ‪( content‬المرتبط مع دخ ل المس تخدم) في ح ال ح دث أي تغي ير على قيمت ه‪ .‬وهك ذا فق د‬

‫وضعت نفس اسم الحقل ‪ content‬ضمن القسم ‪ watch‬كما هو واضح‪ ،‬وأسندت إليه ت ابع‪ .‬ه ذه هي الص يغة‬

‫المعتمدة لمراقبة أي حقل‪.‬‬

‫المض ّمن ‪setTimeout‬‬


‫الذي يحدث ضمن الخاصية المراقبة بسيط في هذا المثال‪ .‬فقد استخدمت التابع ُ‬

‫لزيادة قيمة الحقل ‪ counter‬بعد ثانيتين في ح ال ح دث أي تغي ير علي ه‪ .‬اعتم دت ه ذا األس لوب‪ ،‬لكي ألفت‬

‫تمام ا عن التع ديالت ال تي يُ جريه ا‬


‫انتباه ك إلى أنّ عملي ة الع د تح دث بش كل غ ير م تزامن‪ ،‬وبش كل منفص ل ً‬
‫المستخدم ضمن مربّع النص‪.‬‬

‫ّ‬
‫نفذ التطبيق السابق‪ ،‬وابدأ بالكتابة والتعديل ض من مر ّب ع النص‪ .‬س تالحظ أنّ قيم ة الع دّ اد س تتزايد بينم ا‬

‫تجري التع ديالت ض من مر ّب ع النص‪ ،‬ولكن ه ذا التزاي د ال يح دث ف و ًرا‪ ،‬إ ّنم ا بت أخير زم ني ق دره ث انيتين عن‬

‫التعديالت الفعلية‪.‬‬

‫المتغير المؤقت ‪ tmp‬لتخزين المرجع ‪ this‬قبل استخدام التابع ‪setTimeout‬‬


‫ّ‬ ‫الحظ أ ّنني استخدمت‬

‫ضمن الخاصية المراقبة‪.‬‬

‫نفذ ضمن ّ‬
‫مغلف (‪ )Closure‬وبالتالي لن ُتش ير الكلم ة ‪ this‬إذا‬ ‫البرمجية ستُ ّ‬
‫ّ‬ ‫والسبب في ذلك أنّ الشيفرة‬
‫ً‬
‫سابقا‪.‬‬ ‫خدمت مباشر ًة إلى كائن ‪ Vue.js‬كما اعتدنا‬
‫استُ ِ‬

‫‪ 4.5‬تثبيت قالب جديد باستخدام )(‪$mount‬‬

‫اس تخدمنا في جمي ع األمثل ة ال تي تعاملن ا معه ا ح تى اآلن القس م ‪ el‬من ك ائن ‪ Vue.js‬لتحدي د العنص ر‬
‫ّ‬
‫سيمثل الق الب ‪ template‬ال ذي س يعمل التط بيق على تعديل ه والتعام ل مع ه كم ا وس بق أن‬ ‫المستَ هدف الذي‬
‫ُ‬

‫أوضحنا من قبل (راجع "فهم قوالب ‪ "Vue.js‬من الفصل الثاني)‪.‬‬

‫‪53‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫إاّل أ ّن ه يمكن االس تغناء عن القس م ‪ el‬بش كل كام ل‪ ،‬إذا لم نكن نع رف مس ً‬


‫بقا العنص ر ال ذي سنس تهدفه‬

‫تلقائيا )(‪ $mount‬ال ذي ين وب عن‬


‫ًّ‬ ‫ّ‬
‫المول د‬ ‫(وبالتالي ال نعرف القالب)‪ .‬في ه ذه الحال ة يمكن اس تخدام الت ابع‬

‫القسم ‪ el‬في هذه الحالة‪ ،‬وذلك في اللحظة التي نريد فيها الربط م ع عنص ر مح دّ د‪ .‬انظ ر إلى المث ال البس يط‬

‫التالي لتوضيح هذه الفكرة‪:‬‬

‫>"‪<div id="app‬‬
‫}}‪{{title‬‬
‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫{ ‪data:‬‬
‫'!‪title:'Hello‬‬
‫}‬
‫)}‬

‫الحظ أ ّنني قد حذفت القسم ‪ ،el‬وهكذا‪ ،‬وعند تنفيذ المثال السابق ستحصل على الخرج التالي‪:‬‬

‫}}‪{{title‬‬

‫أي أنّ ‪ Vue.js‬لم يعرف في هذه الحالة الق الب ال ذي س يتعامل مع ه‪ .‬لح ل ه ذه المش كلة وإظه ار الرس الة‬

‫سطرا واحدً ا فقط إلى شيفرة ‪ JavaScript‬السابقة‪ ،‬لتصبح الشيفرة على النحو التالي‪:‬‬
‫ً‬ ‫المناسبة‪ ،‬سنضيف‬

‫{(‪var app = new Vue‬‬


‫{ ‪data:‬‬
‫'!‪title:'Hello‬‬
‫}‬
‫;)}‬

‫;)'‪app.$mount('#app‬‬

‫المس تهدَ ف (وه و ‪ #app‬في مثالن ا) ليعم ل‬


‫معرف العنصر ُ‬
‫ومررنا له ّ‬ ‫ً‬
‫إذا فقد استخدمنا التابع )(‪ّ $mount‬‬
‫عندها ‪ Vue.js‬على ربط (‪ )mount‬القالب المراد التعامل معه‪ ،‬وبالتالي إظهار الرسالة المناسبة للمستخدم‪.‬‬

‫المهمة جدًّ ا في ‪ Vue.js‬حيث أ ّنها تسمح ببناء المكوّ نات ال تي س نتحدّ ث عنه ا‬
‫ّ‬ ‫ُتعتبر هذه الميزة‪ ،‬من المزايا‬

‫بعد قليل‪.‬‬

‫‪ 4.6‬فصل القالب عن عنصـر ‪ HTML‬المستهدف‬

‫الموس ع م ع المكوّ ن ات‪ ،‬وهي إمكاني ة ع دم كتاب ة‬


‫ّ‬ ‫ً‬
‫الحق ا عن د العم ل‬ ‫مهمة أخرى سنحتاج إليها‬
‫توجد ميزة ّ‬

‫المستَ هدَ ف‪ .‬بمع نى آخ ر‪ ،‬يمكن فص ل ش يفرة ‪ HTML‬ال تي‬


‫شيفرة ‪ HTML‬التي (تعبر عن القالب) ضمن العنصر ُ‬

‫‪54‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫ُتع ّبر عن القالب عن العنصر ُ‬


‫المستَ هدَ ف‪ ،‬ووضعها ض من قس م جدي د ض من ك ائن ‪ .Vue.js‬اس م ه ذا القس م ه و‬

‫بقية األقسام الرئيسية‪ .‬انظر إلى الشكل التالي‪:‬‬


‫‪ template‬ويوضع على نفس المستوى مع ّ‬

‫{(‪var app = new Vue‬‬


‫‪el: "#app",‬‬
‫‪template: "HTML CODE GOES HERE",‬‬
‫{ ‪data:‬‬
‫‪...‬‬
‫‪},‬‬
‫{ ‪computed:‬‬
‫‪...‬‬
‫‪},‬‬
‫{ ‪watch:‬‬
‫‪...‬‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫‪...‬‬
‫}‬
‫)}‬

‫بسيطا ّ‬
‫يوضح هذه الفكرة‪:‬‬ ‫ً‬ ‫لنأخذ مثااًل‬

‫>"‪<div id="app‬‬

‫>‪</div‬‬

‫{(‪var app = new Vue‬‬


‫‪el:'#app',‬‬
‫'>‪template:'<h2>Hsoub Academy</h2‬‬
‫;)}‬

‫المس تَ هدف وه و ف ارغ ب الطبع‪ .‬أم ا بالنس بة لش يفرة‬


‫بالنسبة لشيفرة ‪ HTML‬فهي ال تحتوي س وى العنص ر ُ‬
‫ً‬
‫وأيض ا‬ ‫المس تهدَ ف عن طري ق ‪،el‬‬
‫عرفن ا ض منه العنص ر ُ‬
‫‪ JavaScript‬فهي تحت وي على ك ائن ‪ Vue.js‬بس يط‪ّ ،‬‬
‫يطا‬
‫وضعنا القالب الذي نري د التعام ل مع ه ض من القس م ‪ .template‬جعلت ك ود ‪ HTML‬في ه ذا الق الب بس ً‬

‫للغاية بهدف شرح الفكرة فقط‪.‬‬

‫ّ‬
‫نفذ المثال السابق لتحصل على الجملة‪:‬‬

‫‪Hsoub Academy‬‬

‫كليا واستبداله بالتابع )(‪ .$mount‬انظر شيفرة‪ JavaScript‬الجديدة‪:‬‬ ‫يمكن ً‬


‫أيضا االستغناء عن القسم ‪ًّ el‬‬

‫{(‪var app = new Vue‬‬


‫'>‪template:'<h2>Hsoub Academy</h2‬‬

‫‪55‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫;)}‬

‫;)'‪app.$mount('#app‬‬

‫يطا على المث ال األخ ير‪ ،‬بحيث نع رض‬


‫تطويرا بس ً‬
‫ً‬ ‫بعد التنفيذ‪ ،‬ستحصل على النتيجة السابقة‪ .‬اآلن لنجري‬

‫معرف ضمن القسم ‪ .data‬سيكون التعديل ضمن شيفرة ‪ JavaScript‬فقط‪:‬‬


‫قيمة حقل اسمه ‪ّ title‬‬

‫{(‪var app = new Vue‬‬


‫‪template:'<div><h2>Hsoub Academy</h2><p>{{title}}</p></div>',‬‬
‫{‪data:‬‬
‫'!‪title:'Welcome dear user‬‬
‫}‬
‫;)}‬

‫;)'‪app.$mount('#app‬‬

‫الشيفرة السابقة سهلة ومباشرة‪ .‬بعد التنفيذ ستحصل على شكل شبيه بما يلي‪:‬‬

‫النص ي ض من ش يفرة ‪ HTML‬الموج ودة ض من الق الب‪ ،‬وذل ك بس بب‬


‫الح ظ كي ف ح دث االس تبدال ّ‬

‫وجود {{‪ .}}title‬هناك مالحظة بسيطة أخرى حول شيفرة ‪ HTML‬المكتوبة ضمن القالب‪ .‬يجب أن يحتوي‬

‫المسنَد إلى القسم ‪ template‬على عنصر جذر واحد‪ ،‬في مثالنا السابق أنشأت عنصر ‪ div‬لهذا الغرض‪،‬‬
‫القالب ُ‬
‫ووضعت فيه العنصرين ‪ h2‬و ‪ p‬كما هو واضح‪.‬‬

‫أساسيا في بناء المكوّ نات واس تخدامها في ‪ .Vue.js‬في الحقيق ة ال نس تخدم‬


‫ًّ‬ ‫مرة أخرى يُ عد هذا األسلوب‬
‫ّ‬

‫هذا األسلوب كما هو في التطبيقات العملية عاد ًة‪ .‬الذي ّ‬


‫يهمنا هنا هو المفهوم فقط‪.‬‬

‫‪ 4.7‬ما هو المكون (‪)Component‬؟‬

‫عام ة عب ارة عن وح دة برمجي ة مس تقلة ب ذاتها‪ُ ،‬تنج ز عماًل واح دً ا على الغ الب‪ .‬نص ادف‬
‫المكوّ ن بص ورة ّ‬

‫كثيرا في ع الم البرمجي ات‪ .‬وإذا أردت أمثل ة عنه ا في ع الم ال ويب‪ ،‬فج داول البيان ات ال تي تع رض‬
‫ً‬ ‫المكوّ نات‬
‫ً‬
‫وأيضا المساحات الص غيرة الموج ودة على ج انب‬ ‫المعلومات المختلفة للمستخدم مع مزايا الترشيح والترتيب‪،‬‬

‫الصفحة التي تعرض درجة الحرارة الحالية أو أسعار الصرف للعمالت‪ ،‬كلها عبارة عن مكوّ نات‪.‬‬

‫وبص ورة عام ة أي ناحي ة وظيفي ة يمكن أن ُتس تخدَ م بش كل متك رر في نفس المش روع ال برمجي أو في‬
‫برمجية مختلفة يمكن أن ُت ّ‬
‫رشح لتصبح مكو ًنا‪.‬‬ ‫ّ‬ ‫مشاريع‬

‫‪56‬‬
Vue.js ‫أساسيات إطار العمل‬ Vue.js ‫المزيد حول كائن‬

ً
‫ س أبني‬.‫الحق ا‬ ‫سنؤجل ذلك إلى فص ول‬
ّ ‫ حيث‬،‫ ولن ندخل في تفاصيلها‬،‫سنالمس المكوّ نات في هذا الفصل‬

‫ انظ ر إلى‬.‫ وظيفت ه التحوي ل من واح دة الكيل وغرام إلى واح دة الباون د‬،‫في هذا الفص ل مك وّ ن بس يط للغاي ة‬

:‫التطبيق التالي‬

<div id="app">
<weightconverter></weightconverter>
<weightconverter></weightconverter>
<weightconverter></weightconverter>
</div>

var wcComponent = Vue.component('weightconverter', {


template: `<div style='margin-bottom:10px;'>
<input type='text' v-on:input='inputChanged' />
<span>Kg. is equivalent to: <b>{{pounds}}</b> pounds.</span>
</div>`,
data: function() {
return {
pounds: 0
}
},
methods: {
inputChanged: function(event) {
this.pounds = Number(event.target.value) * 2.20462;
}
}
});

// commnet
var app = new Vue({
el: '#app',
components: {
'weightconverter': wcComponent
}
});

ّ
:‫ ستحصل على شكل شبيه بما يلي‬،‫نفذ التطبيق السابق‬

‫األول من ه ذه‬
ّ ‫ لنب دأ بالقس م‬.JavaScript ‫ لننظ ر اآلن إلى ش يفرة‬.‫ بعد قلي ل‬HTML ‫سأتحدث عن شيفرة‬

:Vue.component ‫سجلنا مكوّ ن جديد باستخدام التابع‬


ّ ‫الشيفرة حيث‬

57
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫{ ‪var wcComponent = Vue.component('weightconverter',‬‬


‫>';‪template: `<div style='margin-bottom:10px‬‬
‫>‪<input type='text' v-on:input='inputChanged' /‬‬
‫>‪<span>Kg. is equivalent to: <b>{{pounds}}</b> pounds.</span‬‬
‫‪</div>`,‬‬
‫{ )(‪data: function‬‬
‫{ ‪return‬‬
‫‪pounds: 0‬‬
‫}‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫{ )‪inputChanged: function(event‬‬
‫;‪this.pounds = Number(event.target.value) * 2.20462‬‬
‫}‬
‫}‬
‫;)}‬

‫المتغي ر ‪.wcComponent‬‬
‫ّ‬ ‫كما هو واضح فإ ّنن ا سنس ند المك وّ ن ال ذي يُ رجع ه الت ابع )(‪ component‬إلى‬

‫األول ه و اس م المك وّ ن الم راد إنش اءه‪ ،‬وه و عب ارة عن قيم ة‬


‫يقبل التابع )(‪ component‬وسيطين‪ .‬الوس يط ّ‬

‫أما الوسيط الثاني فهو كائن آخر يحوي إعدادات المكوّ ن‪.‬‬
‫نصية‪ّ ،‬‬
‫ّ‬

‫لو تمعنت النظ ر به ذا الك ائن‪ ،‬فس تجد أ ّن ه يط ابق ك ائن ‪ Vue.js‬قياس ي م ع اختالف واح د بس يط‪ .‬يكمن‬

‫االختالف في القسم ‪ .data‬في كائنات ‪ Vue.js‬ال تي أنش أناها ح تى اآلن ك ّن ا ُنس ند للقس م ‪ data‬ك ائن ع ادي‬

‫أما في الش يفرة الس ابقة فيجب تعري ف القس م ‪data‬‬


‫يحتوي على الحقول المراد التعامل معها ضمن التطبيق‪ّ .‬‬
‫على أ ّنه تابع يُ رجع الكائن الذي يحتوي الحقول المراد التعامل معها‪:‬‬

‫{ )(‪data: function‬‬
‫{ ‪return‬‬
‫‪pounds: 0‬‬
‫}‬
‫}‬

‫ما عدا ذلك تبقى األمور كما هي!‬

‫أما القسم الثاني من شيفرة ‪ JavaScript‬فتحتوي على تعريف كائن ‪ Vue.js‬ع ادي‪ ،‬ولكن م ع وج ود قس م‬
‫ّ‬
‫جديد وهو القسم ‪ .components‬هذا القسم يحتوي على أيّ ة مكونات سيستخدمها التطبيق‪ ،‬وهي في حالتن ا‬

‫عرفناه قبل قليل‪:‬‬


‫هذه المكوّ ن ‪ weightconverter‬الذي ّ‬

‫{(‪var app = new Vue‬‬


‫‪el: '#app',‬‬
‫{ ‪components:‬‬
‫‪'weightconverter': wcComponent‬‬
‫}‬
‫;)}‬

‫‪58‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المزيد حول كائن ‪Vue.js‬‬

‫المتغي ر‬
‫ّ‬ ‫نسجل المكونات التي نريد استخدامها‪ :‬اس م المك وّ ن يلي ه المرج ع ل ه (موج ود ض من‬
‫ّ‬ ‫الحظ كيف‬

‫‪ wcComponent‬بالطبع)‪.‬‬

‫بالنس بة إلى ش يفرة ‪ HTML‬فهي بس يطة‪ ،‬حيث نس تخدم العنص ر الجدي د ‪ weightconverter‬ض من‬

‫شيفرة ‪ HTML‬كما لو أصبح عنصر ‪ HTML‬نظامي‪ .‬اس تخدمت ه ذا العنص ر ثالث م ّرات من ب اب توض يح أ ّن ه‬

‫يمكنك استخدامه في أكثر من مكان ضمن الصفحة‪.‬‬

‫‪ 4.8‬ختام الفصل‬

‫خصوصا لما يليه من فصول‪ .‬سأحيلك بشكل متك ّرر‬


‫ً‬ ‫والمهمة‬
‫ّ‬ ‫غنيا بالمعلومات المتنوّ عة‬
‫لقد كان هذا الفصل ًّ‬
‫إلى هذا الفصل في المستقبل‪ّ ،‬‬
‫كلما اقتضت الضرورة إلى الرجوع إلى مفهوم ُذكر هنا‪ .‬سننتقل في الفصل الت الي‬

‫وسنتوسع بمفهوم المكوّ نات‪.‬‬


‫ّ‬ ‫عملية حقيقية‪،‬‬
‫ّ‬ ‫ّ‬
‫سنتعلم كيفية بناء تطبيقات‬ ‫إلى مستوى جديد‪ ،‬حيث‬

‫‪ 4.9‬تمارين داعمة‬

‫‪ 4.9.1‬تمرين ‪1‬‬

‫أجر تعدياًل على تطبيق تحويل الوحدات الذي بنين اه قب ل قلي ل‪ ،‬بحيث يس مح بالتحوي ل باالتج اهين‪ :‬من‬

‫كيلوغرام إلى باوند‪ ،‬ومن باوند إلى كيلو غرام بشكل مباشر‪.‬‬

‫‪ 4.9.2‬تمرين ‪2‬‬
‫أنشئ مكوّ ًن ا جدي دً ا‪ ،‬عب ارة عن م ّ‬
‫ؤقت زم ني تن ازلي (‪ .)Countdown Timer‬قيمت ه االبتدائي ة دقيق ة‬

‫واحدة‪ ،‬ثم يتناقص حتى يصل للصفر‪ .‬ثم استخدام هذا المكوّ ن ضمن تطبيق بسيط لتجربته فقط‪.‬‬

‫‪59‬‬
‫‪ .5‬مدخل إىل التعامل مع المكونات‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫ّ‬
‫محلي‪.‬‬ ‫تجهيز هيكل التطبيق على حاسوب‬ ‫•‬

‫بناء مكوّ ن جديد‪ :‬مكوّ ن المهام‪.‬‬ ‫•‬

‫تحسين تجربة االستخدام للمكوّ ن‪.‬‬ ‫•‬

‫تمرير وسائط إلى المكوّ نات‪.‬‬ ‫•‬

‫إنشاء أكثر من نسخة من المكون ضمن نفس الصفحة‪.‬‬ ‫•‬

‫إضافة ميزة التصفية لمكوّ ن المهام‪.‬‬ ‫•‬

‫إضافة ميزة مهمة جديدة لمكوّ ن المهام‪.‬‬ ‫•‬

‫عملية‪ .‬س نبني في‬


‫ّ‬ ‫ّ‬
‫سنتعلم كيف نبني المكوّ نات بصورة‬ ‫سنتابع في هذا الفصل التعامل مع المكوّ نات‪ ،‬حيث‬

‫مبس ط‪ .‬اله دف من ه ذا التط بيق ه و التع ّرف على‬


‫بسيطا لكنّه عملي‪ ،‬وهو مكوّ ن إدارة مهام ّ‬
‫ً‬ ‫هذا الفصل مكوّ ًنا‬

‫جي د‪ .‬سيعمل هذا التطبيق البسيط على السماح للمستخدم بعرض بعض المهام ال تي‬
‫أسس بناء المكوّ نات بشكل ّ‬
‫ينوي تنفيذها فيما بع د‪ ،‬م ع إمكاني ة تحدي د فيم ا إذا ك ان ق د أنج ز المه ام أم ال باس تخدام زر اختي ار بس يط‪.‬‬

‫وسنضيف إليه ميزة ترشيح بسيطة إلخفاء أو إظهار المهام المنجزة‪ ،‬باإلضافة إلى إمكانية إضافة مهام جديدة‪.‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫‪ 5.1‬تجهيـز هيكل التطبيق عىل حاسوب محيل‬

‫عم ا اعتدناه في الفصول السابقة‪ .‬كان تركيزنا في الفصول الس ابقة منص ًّبا‬
‫مغايرا ّ‬
‫ً‬ ‫منحى‬
‫المرة ً‬
‫سنسلك هذه ّ‬
‫ّ‬
‫التعلم أو تج ريب‬ ‫جي د في الواق ع عن دما نري د‬
‫على كتابة تطبيقات ‪ vue.js‬ضمن موقع ‪ ،JSFiddle‬وهذا األم ر ّ‬
‫بعض المزايا الخفيفة‪ .‬ولكن عند االنتقال إلى مستوى أعلى ببناء تطبيقات عملية وواقعية‪ ،‬ينبغي علينا بالتأكي د‬

‫االنتقال إلى أدوات تطوير فعً الة ومناسبة‪.‬‬

‫تشتمل هذه األدوات بطبيعة الح ال على خ ادوم نس تخدمه أثن اء بن اء التط بيق‪ ،‬نح اكي من خالل ه س لوك‬

‫الخ واديم الفعلي ة ال تي ستستض يف تطبيقن ا النه ائي ال ذي س يعمل علي ه المس تخدم في نهاي ة المط اف‪ .‬في‬

‫دوم ا بجع ل ظ روف تج ريب التط بيق مماثل ة ق در المس تطاع لم ا س يكون علي ه الوضع‬
‫الحقيق ة‪ ،‬أنص ح ً‬
‫النهائي للتطبيق‪.‬‬

‫البرمجي ة ‪Visual Studio Code‬‬


‫ّ‬ ‫محرر الش يفرة‬
‫ّ‬ ‫سنستخدم في هذا الفصل وغيره من الفصول الالحقة‪،‬‬

‫المحرر الذي ترغب به‪ ،‬أو حتى يمكنك استخدام بيئة تطوير متكاملة‬
‫ّ‬ ‫من ‪ .Microsoft‬يمكنك في الواقع اختيار‬

‫محررات النصوص البرمجية األخرى مثل ‪ Atom‬و ‪ Sublime Text‬و ‪.Brackets‬‬


‫ّ‬ ‫إن أحببت‪ .‬يوجد العديد من‬

‫لتثبيت ‪ Visual Studio Code‬يمكنك زيارة الصفحة التالي ة ‪.code.visualstudio.com/download‬‬

‫يمكنك اختي ار نظ ام التش غيل المناس ب ل ك من األس فل‪ .‬بالنس بة لن ا س نختار النس خة الخاص ة بوين دوز‪ .‬بع د‬

‫نص ب التطبيق مع ترك الخيارات االفتراضية كما هي (قد تحتاج إلى صالحيات مدير النظام)‪.‬‬
‫التنزيل‪ّ ،‬‬

‫الخاص ة ب ه‪ ،‬واعم ل على تث بيت‬


‫ّ‬ ‫بعد تثبيت ‪ Visual Studio Code‬انتق ل إلى اإلض افات ‪Extensions‬‬

‫اإلضافة ‪ Live Server‬التي سنس تخدمها كخ ادوم بس يط‪ .‬يمكن ك الوص ول مباش ر ًة إلى م دير اإلض افات من‬

‫الناحية اليسرى من الشاشة‪ ،‬كما هو ظاهر من الشكل التالي‪ .‬بع د ذل ك أدخ ل اس م اإلض افة‪ Live Server :‬في‬

‫خانة البحث‪ .‬بع د أن يج ده‪ ،‬اخ تره‪ ،‬لتظه ر الناف ذة الخاص ة ب ه كم ا في الش كل الس ابق‪ ،‬ثم انق ر ال زر األخض ر‬

‫‪ Install‬لتثبيته‪ .‬انظر الشكل التالي‪:‬‬

‫‪61‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫من المفيد ً‬
‫أيضا تثبيت اإلضافتين التاليتين‪:‬‬

‫اإلضافة ‪ Vetur‬لتنسيق الشيفرة الخاصة بـ ‪.vue.js‬‬ ‫•‬

‫اإلضافة ‪ HTML5 Boilerplate‬لتنسيق شيفرة ‪.HTML‬‬ ‫•‬

‫اكتب اس م ك ل من ه اتين اإلض افتين في خان ة البحث‪ ،‬واعم ل على تثبيتهم ا كم ا فعلن ا قب ل قلي ل مع‬

‫اإلضافة ‪.Live Server‬‬

‫أنصح بإعادة تشغيل ‪ Visual Studio Code‬عند هذه المرحلة حتى ولو لم يطلب منك ذلك‪.‬‬

‫ّ‬
‫مجل دً ا‬ ‫لنبدأ ببناء مشروعنا! انتقل إلى المكان الذي ترغب فيه بإنشاء المشروع على القرص الصلب‪ ،‬وأنشئ‬

‫مرة أخرة إلى ‪ Visual Studio Code‬ثم اختر األمر ‪Open Folder >- File‬‬
‫سمه ‪ .veujs-mytasks‬انتقل ّ‬
‫ّ‬
‫توا‪.‬‬ ‫ّ‬
‫المجلد الذي أنشأته ًّ‬ ‫واختر‬

‫ّ‬
‫مؤش ر‬ ‫من نافذة المستكشف ‪ Explorer‬الموجودة في الطرف األيسر‪ ،‬الح ظ األيقون تين الص غيرتين‪ ،‬ض ع‬

‫الفأرة للحظات فوق كل منهما لتكتشف وظيفة كل منهما‪ .‬األيقونة األولى من اليسار وظيفتها إنشاء ملف جدي د‬
‫ّ‬
‫المجلد الحالي‪.‬‬ ‫ّ‬
‫مجلد جديد ضمن‬ ‫ّ‬
‫المجلد الحالي‪ ،‬واأليقونة األخرى وظيفتها إنشاء‬ ‫ضمن‬

‫‪62‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫اس تخدم زر مل ف جدي د لتُ نش ئ ملفين‪ ،‬واخ تر االس مين ‪ index.html‬و ‪ app.js‬لهم ا على ال ترتيب‪.‬‬

‫ستحصل في النهاية على شكل شبيه بما يلي‪:‬‬

‫اختر الملف ‪ index.html‬لكي تفتحه‪ ،‬ثم انسخ شيفرة ‪ HTML‬التالية إليه‪:‬‬

‫‪63‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫مدخل إىل التعامل مع المكونات‬

<!DOCTYPE html>
<html>

<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>My Tasks</title>
<meta name='viewport' content='width=device-width, initial-
scale=1'>
</head>

<body>
<h1>Welcome to MyTasks Application</h1>
<p>This application is built to explain how to deal with
components</p>

<div id='app'>
<tasks></tasks>
</div>

<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script src="app.js"></script>
</body>

</html>

‫ وهو‬tasks ‫ األمر الجديد الوحيد فيها هو إضافة العنصر‬،‫ السابقة عبارة عن شيفرة بسيطة‬HTML ‫شيفرة‬

‫أيض ا أ ّنن ا وض عنا ه ذا العنص ر‬


ً ‫ الح ظ‬.‫وسيمثل قائمة المهام ال تي ن رغب ببنائه ا‬
ّ ‫المكوّ ن الذي سنبنيه بعد قليل‬
ً
.‫سابقا‬ ‫ كما اعتدنا‬vue.js ‫ وهو العنصر المستهدَ ف في كائن‬id = 'app' ‫ له الوسم‬div ‫الجديد ضمن عنصر‬

‫ ال ذي‬app.js ‫ باإلض افة إلى مرج ع للمل ف‬vue.js ‫ الح ظ كي ف أض فت م رجعين لمل ف إط ار العم ل‬،‫يرا‬
ً ‫وأخ‬
:‫سيحتوي على الشيفرة البرمجية لكل من المكوّ ن والتطبيق‬

<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script src="app.js"></script>

:‫ انسخ الشيفرة البرمجية التالية إليه‬،app.js ‫لننتقل اآلن إلى الملف‬

Vue.component('tasks', {
template: '<strong><p>{{name}} - Tasks</p></strong>',
data() {
return {
name: 'Husam'
}
}
})

64
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫{(‪new Vue‬‬
‫'‪el: '#app‬‬
‫)}‬

‫البرمجية هنا مماثلة لتلك التي تعاملها معها في الفصل الس ابق‪ ،‬حيث نس ّجل مك وّ ن جدي د باس م‬
‫ّ‬ ‫الشيفرة‬

‫‪ tasks‬بحيث ُنس ند ل ه ق الب بس يط‪ ،‬يع رض اس م الش خص ال ذي سنُس ند إلي ه ه ذه المه ام عن طريق‬

‫ونعين العنصر المستهدف‪.‬‬


‫ّ‬ ‫الخاصية {{‪ }}name‬كما هو واضح‪ .‬ثم ننشئ كائن ‪ vue.js‬بسيط‬

‫انتقل اآلن إلى ناف ذة المستكش ف ‪ ،Explorer‬وانق ر ب زر الف أرة األيمن على المل ف ‪ index.html‬ثم اخ تر‬

‫األمر ‪( Open with Live Server‬تذكّر أننا ثبتنا اإلضافة ‪ Live Server‬قب ل قلي ل)‪ ،‬س يؤدي ذل ك إلى فتح‬

‫ناف ذة أو تب ويب جدي د ض من متص ّفح االن ترنت االفتراض ي ل ديك بحيث يتج ه إلى العن وان‬

‫‪ http://127.0.0.1:5500/index.html‬وه و العن وان م ع المنف ذ االفتراض ي ال ذي ينص ت عن ده‬

‫الخادوم ‪ .Live Server‬ستحصل على شكل شبيه بما يلي‪:‬‬

‫ه ذا دلي ل على أنّ األم ور تس ير على م ا ي رام‪ .‬وأنّ نجحن ا ببن اء الهيك ل الع ام للتط بيق‪ .‬لننتق ل اآلن‬

‫للمرحلة التالية‪.‬‬

‫‪ 5.2‬بناء مكون جديد‪ :‬مكون المهام‬

‫لنب دأ اآلن بالعم ل الفعلي في بن اء المك وّ ن الخ اص بالمه ام‪ ،‬وال ذي أس ميناه ‪ .tasks‬س أنقل أواًل ش يفرة‬

‫‪ HTML‬المسندة للحقل ‪ template‬ضمن المكوّ ن‪ ،‬وأضعها ضمن مكان منفصل أل ّنها ستص بح بع د قلي ل كب يرة‬

‫ومعقدة بعض الشيء لتُ وضع في مكان كهذا‪ِ .‬‬


‫أجر التعديل الت الي في المل ف ‪ app.js‬ض من الحق ل ‪template‬‬

‫للمكوّ ن ليصبح على النحو التالي‪:‬‬

‫'‪template: '#tasks-template‬‬

‫‪65‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫الحظ أنني قد عرضت مكان التعديل فقط طل ًبا لالختصار‪ .‬الجديد هنا أ ّنني وضعت مع ّرف الق الب الجدي د‬

‫ال ذي س يحتوي على الش يفرة‪ .‬انتق ل اآلن إلى المل ف ‪ index.html‬وأض ف الش يفرة التالي ة مباش ر ًة بعد‬

‫المستهدَ ف الخاص بتطبيق ‪:vue.js‬‬


‫عنصر ‪ُ div‬‬

‫>'‪<script type='text/x-template' id='tasks-template‬‬


‫>‪<div‬‬
‫>‪<h3>{{ name }} - Tasks</h3‬‬
‫>‪</div‬‬
‫>‪</script‬‬

‫كم ا ت رى أج ريت بعض التع ديل على ش يفرة ‪ HTML‬ال تي ك انت موج ودة س ً‬
‫ابقا‪ .‬انتق ل اآلن إلى‬

‫المتصفح ثم حدثها (إن لم ُتحدّ ث بشكل تلقائي)‪ ،‬يجب أن تحصل على شكل ق ريب‬
‫ّ‬ ‫الصفحة ‪ index.html‬في‬

‫من الشكل الذي حصلنا عليه في الفقرة السابقة‪.‬‬

‫مخص ص ل ه‪ .‬في ه ذه الحال ة س يكون ض من‬


‫ّ‬ ‫الجدي د هن ا ه و فص ل الق الب ووض عه ض من مك ان‬

‫العنصر ‪ script‬والذي تحمل السمة ‪ type‬له القيمة ‪ text/x-template‬كم ا ه و واض ح‪ .‬باإلض افة ل ذلك‬

‫الحظ كيف جعلت قيمة المعرف ‪ id‬له نفس القيمة التي أسندتها للحقل ‪ template‬ضمن المكوّ ن قبل قليل‪.‬‬

‫من المهم جدًّ ا تحقيق مبدأ الفصل في بناء المكوّ نات‪ .‬أل ّنه كلما أصبح المك وّ ن أك ثر تعقي دً ا كم ا س ترى بع د‬

‫قليل‪ ،‬كلما برزت الحاجة إلى تحقيق مبدأ الفصل بشكل أفضل‪.‬‬

‫ُكسب اآلن مكوّ ننا الوليد بعض المزايا اإلضافية لكي يصبح قاد ًرا على عرض بعض المهام للمستخدم‪ .‬أج ر‬
‫لن ِ‬
‫التعديالت التالية ضمن الملف ‪ app.js‬ليصبح مشابهً ا لما يلي‪:‬‬

‫{ ‪Vue.component('tasks',‬‬
‫‪template: '#tasks-template',‬‬
‫{ )(‪data‬‬
‫{ ‪return‬‬
‫‪name: 'Husam',‬‬
‫[ ‪tasks_list:‬‬
‫‪{ title: "Write an introduction about vue.js‬‬
‫‪components.", done: true },‬‬
‫‪{ title: "Drink a cup of team.", done: false },‬‬
‫‪{ title: "Call Jamil.", done: false },‬‬
‫} ‪{ title: "Buy new book.", done: true‬‬
‫]‬
‫}‬
‫}‬
‫)}‬
‫{(‪new Vue‬‬
‫'‪el: '#app‬‬
‫)}‬

‫‪66‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫مدخل إىل التعامل مع المكونات‬

.‫ يحتوي على بيانات المهام التي أرغب بعرضها‬tasks_list ‫ لقد أضفت حقاًل جديدً ا أسميته‬،‫في الواقع‬

‫ تضم األولى النص الخ اص‬:‫ يحتوي على خاصيتين‬،‫مهم ة ضمن القائمة عبارة عن كائن بسيط‬
ّ ‫الحظ كيف أنّ كل‬

.false ‫ أم ليس بعد‬true ‫أما الثانية فتضم قيمة منطقية تشير إلى أنّ المهمة قد ُن ّفذت‬
ّ ،‫بالمهمة‬

‫ ليص بح مم اثاًل‬،‫ وأجر بعض التع ديالت ال تي س تكون أك بر ه ذه الم رة‬index.html ‫انتقل اآلن إلى الملف‬

:‫لما يلي‬

<!DOCTYPE html>
<html>

<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>My Tasks</title>
<meta name='viewport' content='width=device-width, initial-
scale=1'>

<style>
.tasks-container {
border-width: 1px;
border-style: solid;
display: inline-block;
margin-right: 20px;
padding: 8px;
}

.w3-table-all {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
display: table;
border: 1px solid #ccc
}

.w3-table-all tr {
border-bottom: 1px solid #ddd
}

.w3-table-all tr:nth-child(odd) {
background-color: #fff
}

.w3-table-all tr:nth-child(even) {
background-color: #f1f1f1
}

.w3-table-all td,
.w3-table-all th {

67
Vue.js ‫أساسيات إطار العمل‬ ‫مدخل إىل التعامل مع المكونات‬

padding: 8px 8px;


display: table-cell;
text-align: left;
vertical-align: top
}

.w3-table-all th:first-child,
.w3-table-all td:first-child {
padding-left: 16px
}

.w3-table-all th{
background-color: #d0d0d0;
}
</style>
</head>

<body>
<h1>Welcome to MyTasks Application</h1>
<p>This application is built to explain how to deal with
components</p>

<div id='app'>
<tasks></tasks>
</div>

<script type='text/x-template' id='tasks-template'>


<div class='tasks-container'>
<table class='w3-table-all'>
<colgroup>
<col style="width:15%">
<col style="width:85%">
</colgroup>
<tbody>
<tr>
<th colspan="2">
<center>{{ name }} - Tasks</center>
</th>
</tr>
<tr>
<td>
<strong>Done</strong>
</td>
<td>
<strong>Title</strong>
</td>
</tr>

<tr v-for="task in tasks_list" v-


bind:key="task.title">
<td>
{{ task.done }}

68
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫>‪</td‬‬
‫>‪<td‬‬
‫}} ‪{{ task.title‬‬
‫>‪</td‬‬
‫>‪</tr‬‬
‫>‪</tbody‬‬
‫>‪</table‬‬
‫>‪</div‬‬
‫>‪</script‬‬

‫>‪<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script‬‬
‫>‪<script src="app.js"></script‬‬
‫>‪</body‬‬

‫>‪</html‬‬

‫ّ‬
‫متصفح اإلنترنت لديك لتحصل على شكل شبيه بما يلي‪:‬‬ ‫حدّ ث الصفحة ‪ index.html‬ضمن‬

‫نسبيا‪ ،‬إاّل أنّ األمر في الحقيقة ليس كذلك‪ .‬ح دثت نس بة‬
‫ً‬ ‫ّ‬
‫ومعقدة‬ ‫ربما تشعر أنّ الشيفرة قد أصبحت كبيرة‬

‫كب يرة من التع ديالت عن دما أض فت عنص ر التنس يق ‪ style‬م ع تنس يقاته إلى المل ف ‪ .index.html‬ك ان من‬

‫األفضل وضع تنسيقات ‪ CSS‬ضمن ملف مستقل‪ ،‬وهذا ما سأعمل عليه بع د قلي ل‪ .‬التنس يقات المس تخدمة هن ا‬

‫استعرتها من موقع ‪ W3Schools‬الشهير‪ .‬باإلضافة إلى ذلك‪ ،‬الحظ أ ّنني قد استخدمت عنصر الج دول ‪table‬‬

‫الموجه‬
‫ّ‬ ‫البرمجي ة واض حة‪ ،‬باس تثناء‬
‫ّ‬ ‫لع رض قائم ة المه ام‪ .‬ع دا عن ذل ك‪ ،‬أعتق د أنّ معظم الش يفرة‬

‫الجديد‪:v-bind:key :‬‬

‫‪69‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫>"‪<li v-for="task in tasks_list" v-bind:key="task.title‬‬

‫موج ه آخ ر مس ً‬
‫بقا‪ ،‬وه و ‪ v-bind:href‬وذل ك في الفص ل الث اني (اس تخدام‬ ‫ّ‬ ‫في الواقع لقد استخدمنا‬

‫‪ vue.js‬للتعام ل م ع ‪ .)DOM‬لكنن ا سنس تخدم الي وم الكلم ة ‪ key‬ب داًل من ‪ .href‬الس تخدام ‪v-bind:key‬‬

‫يرا‪ .‬نس تخدم ه ذا‬


‫وخصوص ا عن دما يك ون حجم البيان ات كب ً‬
‫ً‬ ‫مهمة ّ‬
‫تتمث ل في األداء (‪،)Performance‬‬ ‫مزيّ ة ّ‬

‫للموجه التكراري ‪.for‬‬


‫ّ‬ ‫الموجه لتعيين مفتاح ربط ‪key‬‬
‫ّ‬

‫من الواضح أ ّنه في الوضع الحالي‪ ،‬من غير الممكن لنا أن نع دّ ل على أي مهم ة بحيث تص بح ّ‬
‫منف ذة أو غ ير‬
‫ّ‬
‫منفذة‪ .‬سنعمل في الفقرة التالية على تحسين تجربة االستخدام‪ ،‬بتوفير إمكانية إجراء هذه التعديالت‪.‬‬

‫‪ 5.3‬تحسني تجربة االستخدام للمكون‬

‫سنعمل اآلن على السماح للمستخدم بتعديل حالة المهم ة وذل ك بإض افة عنص ر اختي ار ‪ .Checkbox‬قب ل‬

‫ذلك دعنا ننقل تنسيقات ‪ CSS‬إلى ملف منفصل‪ .‬أنشئ ملف جديد ضمن نافذة المستكشف في ‪Visual Studio‬‬

‫‪ Code‬لهذا الغرض ولنسمه ‪.tasks.css‬‬

‫انق ل محتوي ات العنص ر ‪ style‬في المل ف ‪ index.html‬إلى ملفن ا الجدي د‪ ،‬ثم اح ذف العنص ر ‪.style‬‬

‫الستخدام التنسيقات ضمن الملف الجديد‪ ،‬أضف مرجعً ا إليه في المل ف ‪ index.html‬ض من القس م ‪ head‬على‬

‫النحو التالي‪:‬‬

‫>"‪<link rel="stylesheet" href="tasks.css‬‬

‫تغييرا ضمن الشيفرة البرمجية للق الب فق ط‪ ،‬وتحدي دً ا بالقس م الخ اص بإنه اء أو ع دم إنه اء‬
‫ً‬ ‫سنجري اآلن‬

‫المهمة أي في القسم ‪ Done‬فقط‪ .‬استبدل بالشيفرة التالي‪:‬‬

‫‪...‬‬
‫>‪<td‬‬
‫}} ‪{{ task.done‬‬
‫>‪</td‬‬
‫‪...‬‬

‫الشيفرة الجديدة التالية‪:‬‬

‫‪...‬‬
‫>‪<td‬‬
‫>‪<input type="checkbox" v-model="task.done"/‬‬
‫>‪</td‬‬
‫‪...‬‬

‫‪70‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫أعد تحديث الصفحة لتحصل على شكل شبيه بما يلي‪:‬‬

‫الحظ أ ّنني قد استخدمت الربط ثنائي االتجاه "‪ v-model="task.done‬للربط الثنائي للبيانات‪.‬‬

‫االختصارات في ‪vue.js‬‬

‫بالموجه ‪ .v-on‬أي أنّ ‪ v-on:click‬مثاًل سيصبح‪ .@:click :‬وبنفس‬


‫ّ‬ ‫دوما استبدال الرمز @‬
‫يمكن ً‬
‫الموجه موجود‪ .‬فمثاًل يمكن‬
‫ّ‬ ‫الموجه ‪ v-bind‬بالكامل‪ ،‬وسيفهم ‪ vue.js‬أنّ هذا‬
‫ّ‬ ‫األسلوب‪ ،‬يمكننا حذف‬

‫كتابة ‪ :href‬فقط بداًل من ‪ ،v-bind:href‬وكتابة ‪ :key‬بداًل من ‪.v-bind:key‬‬

‫‪ 5.4‬تمرير وسائط إىل المكونات‬

‫لعلك قد الحظت أ ّننا استخدمنا بيانات ثابتة في التعامل مع مكوّ ن المهام‪ .‬ولكن في الواقع العملي سنحتاج‬
‫إلى أن تكون هذه البيانات قابلة للتغيير كما هو واضح‪ ,‬لهذا الهدف ّ‬
‫توفر المكوّ نات في ‪ vue.js‬ميزة الخص ائص‬

‫الممرر إلى المكوّ ن عند إنشاءه لهذه الغاي ة‪.‬‬


‫ّ‬ ‫‪ ،props‬حيث يمكن وضع قسم جديد اسمه ‪ props‬ضمن الوسيط‬

‫في الحقيقة للخصائص الموجودة ضمن القسم ‪ props‬فوائد مهم ة عن دما س نتحدث عن المكون ات المتداخل ة‬
‫ً‬
‫الحق ا في الفصل التالي‪ ،‬حيث سنستخدمها لتمرير البيانات من المكون األساسي إلى المكون الفرعي‪.‬‬

‫لنبدأ بإجراء التعديالت الالزمة‪ .‬أجر التعديالت التالية في الملف ‪ app.js‬ليصبح على الشكل التالي‪:‬‬

‫‪71‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫{ ‪Vue.component('tasks',‬‬
‫‪template: '#tasks-template',‬‬
‫{ ‪props:‬‬
‫‪name: String,‬‬
‫‪tasks_list: Array‬‬
‫}‬
‫)}‬

‫{(‪new Vue‬‬
‫'‪el: '#app‬‬
‫)}‬

‫لقد أزلت قسم ‪ data‬وأضفت بداًل منه القسم ‪( props‬يمكن بالطبع أن يكون ا معً ا بنفس ال وقت)‪ .‬ع رفت‬

‫أمررها للمكوّ ن ضمن القسم ‪ props‬بالشكل التالي‪:‬‬


‫الوسائط التي يمكن أن ّ‬

‫{ ‪props:‬‬
‫‪name: String,‬‬
‫‪tasks_list: Array‬‬
‫}‬

‫الوسيط األول هو ‪ name‬وهو من النوع ‪ String‬أي نص‪ ،‬والوس يط الث اني ه و ‪ tasks_list‬وه و من‬

‫نوع ‪ Array‬أي مصفوفة كما هو واضح‪ .‬بالنسبة لطريقة االستخدام فهي سهلة ج دا‪ .‬أج ر التع ديل الت الي على‬

‫الوسم ‪ tasks‬ضمن الملف ‪:index.html‬‬

‫‪<tasks name='Husam' v-bind:tasks_list='[{ title: "Write an‬‬


‫‪introduction about vue.js components.", done: true },‬‬
‫‪{ title: "Drink a cup of team.", done: false },‬‬
‫‪{ title: "Call Jamil.", done: false },‬‬
‫>‪{ title: "Buy new book.", done: true }]'></tasks‬‬

‫مررت البيانات إلى المكوّ ن كما لو أ ّنها وسوم عادية‪ .‬الشيء الوحيد الملفت للنظ ر ه و أ ّن ني ق د اس تخدمت‬
‫ّ‬

‫الموجه ‪ v-bind:‬عند تمرير المصفوفة ‪ tasks_list‬وهذا األمر إلزامي عن د تمري ر وس ائط ديناميكي ة إلى‬

‫المكوّ نات‪ ،‬في حين أ ّنه ال يجب استخدام هذا الموج ه عن د تمري ر وس ائط نص ية س اكنة كم ا فعلن ا عن د تمري ر‬

‫الوسيط ‪.name‬‬

‫الحظ أنه يوجد العديد من أنواع الوسائط التي يمكن تمريرها إلى المكونات وهي‪:‬‬

‫‪String, Number, Boolean, Array, Object, Function, Promise‬‬

‫‪72‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫‪ 5.5‬إنشاء أكرث من نسخة من المكون ضمن نفس الصفحة‬

‫لنعم ل اآلن على اس تخدام أك ثر من مك ون واح د ض من الص فحة لنكتش ف ق وة المكون ات‪ .‬انس خ ش يفرة‬

‫‪ HTML‬األخيرة والصقها مرة أخرى مع إجراء بعض التعديالت البسيطة عليها‪:‬‬

‫‪<tasks name='My house' v-bind:tasks_list='[{ title: "Do a cleaning for‬‬


‫‪windows.", done: false},‬‬
‫‪{ title: "Bring some vegetables and fruits.", done: true},‬‬
‫>‪{ title: "Wash clothes", done: false}]'></tasks‬‬

‫حدث الصفحة ‪ index.html‬من جديد لتحصل على شكل شبيه بما يلي‪:‬‬

‫يمكننا بسهولة أن نستخدم هذا المكون في المكان الذي نحتاج إلي ه في الص فحة‪ ،‬كم ا يمكن مش اركته م ع‬

‫اآلخرين‪ .‬مازال هناك الكث ير للتح دث عن ه ح ول المكوّ ن ات‪ ،‬س نتناول في الفص ول الالحق ة العدي د من المزاي ا‬

‫الخاصة بها‪ ،‬والطرق المثالية للتعامل معها‪ .‬يمكنك الحصول على الشيفرات الكاملة بالملفات الثالث ة ال تي عملن ا‬

‫عليها في هذا الفصل في المقاطع الثالثة التالية‪:‬‬

‫الملف ‪:index.html‬‬ ‫•‬

‫>‪<!DOCTYPE html‬‬
‫>‪<html‬‬

‫>‪<head‬‬
‫>'‪<meta charset='utf-8‬‬
‫>'‪<meta http-equiv='X-UA-Compatible' content='IE=edge‬‬
‫>‪<title>My Tasks</title‬‬
‫‪<meta name='viewport' content='width=device-width, initial-‬‬
‫>'‪scale=1‬‬
‫>"‪<link rel="stylesheet" href="tasks.css‬‬
‫>‪</head‬‬

‫>‪<body‬‬

‫‪73‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫مدخل إىل التعامل مع المكونات‬

<h1>Welcome to MyTasks Application</h1>


<p>This application is built to explain how to deal with
components</p>

<div id='app'>
<tasks name='Husam' v-bind:tasks_list='[{ title: "Write an
introduction about vue.js components.", done: true },
{ title: "Drink a cup of tea.", done: false },
{ title: "Call Jamil.", done: false },
{ title: "Buy a new book.", done: true }]'></tasks>

<tasks name='My house' v-bind:tasks_list='[{ title: "Clean


windows.", done: false},
{ title: "Bring some vegetables and fruits.", done: true},
{ title: "Wash clothes", done: false}]'></tasks>
</div>

<script type='text/x-template' id='tasks-template'>


<div class='tasks-container'>
<table class='w3-table-all'>
<colgroup>
<col style="width:15%">
<col style="width:85%">
</colgroup>
<tbody>
<tr>
<th colspan="2">
<center>{{ name }} - Tasks</center>
</th>
</tr>
<tr>
<td>
<strong>Done</strong>
</td>
<td>
<strong>Title</strong>
</td>
</tr>

<tr v-for="task in tasks_list" v-


bind:key="task.title">
<td>
<input type="checkbox" v-
model="task.done"/>
</td>
<td>
{{ task.title }}
</td>
</tr>
</tbody>

74
Vue.js ‫أساسيات إطار العمل‬ ‫مدخل إىل التعامل مع المكونات‬

</table>
</div>
</script>

<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script src="app.js"></script>
</body>

</html>

:tasks.css ‫الملف‬ •

.tasks-container {
border-width: 1px;
border-style: solid;
display: inline-block;
margin-right: 20px;
padding: 8px;
}

.w3-table-all {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
display: table;
border: 1px solid #ccc
}

.w3-table-all tr {
border-bottom: 1px solid #ddd
}

.w3-table-all tr:nth-child(odd) {
background-color: #fff
}

.w3-table-all tr:nth-child(even) {
background-color: #f1f1f1
}

.w3-table-all td,
.w3-table-all th {
padding: 8px 8px;
display: table-cell;
text-align: left;
vertical-align: top
}

.w3-table-all th:first-child,
.w3-table-all td:first-child {
padding-left: 16px

75
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫}‬

‫{‪.w3-table-all th‬‬
‫;‪background-color: #d0d0d0‬‬
‫}‬

‫الملف ‪:app.js‬‬ ‫•‬

‫{ ‪Vue.component('tasks',‬‬
‫‪template: '#tasks-template',‬‬
‫{ ‪props:‬‬
‫‪name: String,‬‬
‫‪tasks_list: Array‬‬
‫}‬
‫)}‬

‫{(‪new Vue‬‬
‫'‪el: '#app‬‬
‫)}‬

‫‪ 5.6‬إضافة ميـزة الرتشيح لمكون المهام‬

‫كثيرا ما ستحتاج في التطبيقات التي س تكتبها إلى م يزة الترش يح بص رف النظ ر عن ن وع التط بيق ال ذي‬
‫ً‬
‫تبنيه‪ .‬سنتناول في هذه الفقرة كيفية إضافة هذه الميزة إلى مكوّ ن المهام‪.‬‬

‫رغم أنّ هذه الميزة بسيطة‪ ،‬وتنحصر وظيفتها في إخفاء (أو إظهار) المهام المنجزة‪ ،‬إاّل أ ّنها ستعطيك فكرة‬

‫جيدة عن كيفية عمل مثل هذه المزايا‪.‬‬

‫س نكمل على الش يفرة األخ يرة لتط بيق المه ام‪ .‬س تكون م يزة التص فية على ش كل ص ندوق‬

‫اختي ار ‪ Checkbox‬موج ود أعلى قائم ة المه ام‪ .‬عن دما يخت اره المس تخدم ف إنّ المه ام المنج زة س تختفي‪،‬‬

‫والعكس صحيح‪.‬‬

‫سنبدأ بإضافة عنص ر االختي ار ه ذا إلى المل ف ‪ index.html‬ض من القس م الخ اص بق الب المك وّ ن‪ .‬أض ف‬

‫الشيفرة التالية بعد الوسم <'‪ >div class='tasks-container‬مباشرة‪:‬‬

‫‪<input id="hide_cmp_tasks" type="checkbox" v-‬‬


‫>‪model="hide_completed_tasks"/‬‬
‫>‪<label for="hide_cmp_tasks">Hide completed tasks</label‬‬

‫الح ظ معي أنّ ه ذا العنص ر مرتب ط بحق ل اس مه ‪ hide_competed_tasks‬س نعرفه بع د قلي ل ض من‬

‫مكوّ ن المهام‪.‬‬

‫‪76‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫افتح اآلن الملف ‪ app.js‬وأض ف القس مين ‪ data‬و ‪ computed‬للمك وّ ن ‪ .tasks‬س يحتوي قس م ‪data‬‬

‫على تعريف الحقل ‪ hide_competed_tasks‬المرتب ط بعنص ر ص ندوق االختي ار ال ذي أض فناه قب ل قلي ل‪،‬‬

‫وسيكون من النوع المنطقي ‪ ،Boolean‬في حين أنّ القسم ‪ computed‬فس يحتوي على الخاص ية المحس وبة‬

‫‪ .filtered_tasks‬سيصبح المكوّ ن ‪ tasks‬على الشكل التالي‪:‬‬

‫{ ‪Vue.component('tasks',‬‬
‫‪template: '#tasks-template',‬‬
‫{ ‪props:‬‬
‫‪name: String,‬‬
‫‪tasks_list: Array,‬‬
‫‪},‬‬
‫{ )(‪data‬‬
‫{ ‪return‬‬
‫‪hide_completed_tasks: false‬‬
‫}‬
‫‪},‬‬
‫{ ‪computed:‬‬
‫{ )(‪filtered_tasks‬‬
‫? ‪return this.hide_completed_tasks‬‬
‫;‪this.tasks_list.filter(t => !t.done) : this.tasks_list‬‬
‫}‬
‫}‬
‫)}‬

‫إذا تأملت معي محتويات الخاصية المحسوبة ‪ filtered_tasks‬فستجدها عبارة عن ش يفرة برمجي ة‬

‫بسيطة لتصفية المهام المنجزة من خالل اختبار قيمة الخاصية ‪ .done‬أي أنّ عملية التصفية ستحدث في ه ذا‬

‫بناء على كون الحقل ‪ hide_completed_tasks‬يحمل القيمة ‪ true‬أو ‪.false‬‬


‫المكان تحديدً ا ً‬

‫ه ذا ك ّل ش يء! إذا أحببت اآلن‪ ،‬انق ر ب زر الف أرة األيمن على المل ف ‪ index.html‬الموج ود ض من ناف ذة‬

‫المستكشف ‪ ،Explorer‬واختر األمر ‪ Open with Live Server‬كما اعتدنا من قبل‪ .‬ستحصل على شكل‬

‫شبيه بما يلي‪:‬‬

‫‪77‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫متكرر على صندوق االختي ار ‪ Hide completed tasks‬لتج د كي ف أنّ المه ام‬
‫ّ‬ ‫جرب اآلن أن تنقر بشكل‬
‫المنجزة تختفي وتظهر ً‬
‫وفقا لذلك‪.‬‬

‫‪ 5.7‬إضافة مزية مهمة جديدة لمكون المهام‬


‫ً‬
‫مسبقا‪ .‬سأضع أسفل قائم ة المه ام عنص ر‬ ‫لنحسن مكوّ ن المهام بميزة إضافة مهمة جديدة للمهام الموجودة‬
‫إدخ ال نص ي م ع زر لإلض افة‪ .‬و سأض يف ً‬
‫أيض ا بعض اللمس ات باس تخدام ‪ CSS‬لكي أحص ل على مظهر‬

‫مقبول لهما‪.‬‬

‫افتح الملف ‪ tasks.css‬وأضف أصناف ‪ CSS‬التالية له‪:‬‬

‫{ ‪.add-task-container‬‬
‫;‪position: relative‬‬
‫;‪margin-top: 10px‬‬
‫}‬

‫{‪.add-task-container div‬‬
‫;‪position: absolute‬‬
‫;‪top: 0‬‬
‫;‪right: 60px‬‬
‫;‪left: 45px‬‬
‫}‬

‫{‪.add-task-container div input‬‬


‫;‪width: 100%‬‬

‫‪78‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫مدخل إىل التعامل مع المكونات‬

.add-task-container input{
position: absolute;
width:50px;
top: 0;
right: 0;
}

‫ وأضف الشيفرة التالية بعد نهاية وسم اإلغالق للجدول مباشر ًة (ض من‬index.html ‫انتقل اآلن إلى الملف‬

:)‫القالب الخاص بمكوّ ن المهام‬

<div class="add-task-container">
<span>Task: </span>
<div>
<input type="text" v-model="new_task_text"/>
</div>
<input type="button" value="Add" v-on:click="add_new_task"/>
</div>

‫ واح رص على أن تك ون محتويات ه‬،‫ افتح هذا المل ف‬.app.js ‫حان اآلن دور الشيفرة البرمجية ضمن الملف‬

:‫مطابقة لما يلي‬

Vue.component('tasks', {
template: '#tasks-template',
props: {
name: String,
tasks_list: Array,
},
data() {
return {
hide_completed_tasks: false,
new_task_text : ""
}
},
computed: {
filtered_tasks() {
return this.hide_completed_tasks ?
this.tasks_list.filter(t => !t.done) : this.tasks_list;
}
},
methods: {
add_new_task(event) {
this.tasks_list.push({ title: this.new_task_text, done:
false });
this.new_task_text = "";
}
}
})

79
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫{(‪new Vue‬‬
‫'‪el: '#app‬‬
‫)}‬

‫الجديد هنا هو أ ّنني أضفت حقاًل جدي دً ا إلى القس م ‪ data‬وأس ميته ‪ new_task_text‬س يرتبط بمرب ع‬

‫النص الذي سندخل من خالله عنوان المهمة‪ .‬كم ا ق د أض فت القس م ‪ methods‬وع رفت ض منه الت ابع الجدي د‬

‫سيستدعى عند نقر زر إضافة المهمة‪.‬‬


‫‪ ،add_new_task‬وهذا التابع الذي ُ‬

‫الشيفرة البرمجية الموجودة في هذا التابع بسيطة‪ ،‬فهي تعمل على إض افة المهم ة الجدي دة إلى مص فوفة‬

‫تفرغ عنوان المهمة من جديد الستقبال المهمة التالية‪.‬‬


‫المهام ‪ tasks_list‬ثم ّ‬

‫جرب كتابة المهمة التالية‪ My new task! :‬ضمن مربع النص‪ ،‬ثم انق ر ال زر ‪ .Add‬ستحص ل على ش كل‬

‫شبيه بما يلي‪:‬‬

‫‪80‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫مدخل إىل التعامل مع المكونات‬

‫‪ 5.8‬ختام الفصل‬

‫بدأنا في هذا الفصل بالولوج في عالم المكوّ نات‪ ،‬إذ يُ عتبر ما تناولناه في هذا الفصل مقدّ مة بس يطة إاّل أنه ا‬

‫مهمة في التعامل مع المكونات حيث تعلمنا كيف نبني هيكاًل لتط بيق بس يط على حاس وبنا الشخص ي‪ ،‬وكيفي ة‬
‫ّ‬
‫وتعلمنا أساسيات فصل ش يفرة‬ ‫مبسط لمحاكاة عمل التطبيق بشكل فعلي‪ ،‬كما بنينا مكوّ ًنا مفيدً ا‪،‬‬
‫إضافة خادوم ّ‬
‫‪ vue.js‬عن ب اقي التط بيق‪ .‬هنال ك المزي د ّ‬
‫لتعلم ه ح ول المكوّ ن ات وح ول ‪ vue.js‬بص ورة عام ة في الفص ول‬

‫القادمة إن شاء اهلل‪.‬‬

‫‪ 5.9‬تمارين داعمة‬

‫ابتداء من‬
‫ً‬ ‫أنشئ مكوّ ًنا باسم ‪ vu-countdowntimer‬وظيفته العد التنازلي بمقدار ثانية واحدة كل ّ‬
‫مرة‪،‬‬

‫قيمة محددة يمكن تمريرها للمكون‪ ،‬وحتى الصفر‪.‬‬

‫‪81‬‬
‫‪ .6‬التعمق في مكونات ‪Vue.js‬‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫بناء تطبيق نموذجي (مشروبات حسوب)‪.‬‬ ‫•‬

‫إضافة وسائل تنقيح متطوّ رة لتطبيقات ‪.Vue.js‬‬ ‫•‬

‫المكوّ نات المتداخلة‪.‬‬ ‫•‬

‫محليا وتسجيلها على المستوى العام‪.‬‬


‫ًّ‬ ‫تسجيل المكوّ نات‬ ‫•‬

‫تحديد المكوّ ن الذي اختاره المستخدم‪.‬‬ ‫•‬

‫مخصصة‪.‬‬
‫ّ‬ ‫التخاطب بين المكوّ نات باستخدام أحداث‬ ‫•‬

‫ّ‬
‫المتعلق ة به ا‪ ،‬وال تي‬ ‫نتعرف على المزي د من المزاي ا‬
‫ّ‬ ‫ّ‬
‫سنتعلم في هذا الفصل المزي د عن المكوّ ن ات‪ ،‬حيث س‬

‫ستساعدنا على بناء مكوّ نات عملية ومفيدة‪ .‬سنبدأ هذا الفصل ببناء تطبيق نموذجي س يكون الهيك ل األساس ي‬

‫نتعرف على كيفي ة اس تخدام أدوات تنقيح‬


‫ّ‬ ‫ال ذي سنس تخدمه ّ‬
‫لتعلم المزاي ا الجدي دة ح ول المكوّ ن ات‪ ،‬ثم س‬
‫ّ‬
‫سنتعلم كيفي ة‬ ‫نتعرف على المكوّ نات المتداخلة وكيفية استخدامها‪ ،‬ثم‬
‫ّ‬ ‫خصيصا لـ ‪ ،Vue.js‬ثم‬
‫ً‬ ‫متطوّ رة مكتوبة‬
‫ّ‬
‫بتعلم كيفي ة التخ اطب بين‬ ‫محلي ا وعلى المس توى الع ام في التط بيق‪ ،‬ونختم‬
‫ً‬ ‫تس جيل المكوّ ن ات‬

‫المكوّ نات المتداخلة‪.‬‬


‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫‪ 6.1‬بناء تطبيق نموذجي (مرشوبات حسوب)‬

‫سنبني من أجل هذا الفصل تطبيق نموذجي بسيط لكي نطبق عليه األفكار التي سنتاولها هنا‪ .‬س يكون ه ذا‬

‫التطبيق عبارة عن واجهة بسيطة لمحل افتراض ي بي ع مش روبات متنوع ة‪ .‬س يع ّبر المك وّ ن في ه ذه الم رة عن‬

‫معين من قائمة المشروبات المتاحة‪.‬‬


‫مشروب ّ‬

‫انظر إلى الشكل النهائي المقترح‪:‬‬

‫ّ‬
‫مجل دً ا جدي دً ا س ّمه ‪hsoub-drinks‬‬ ‫كما ترى فإ ّنني أستخدم اللغة العربية في التطبيق هذه الم ّرة! أنش ئ‬

‫سنستخدمه لوضع ملفات المشروع ضمنه‪.‬‬

‫حاليا أسميته ‪ drink‬أي مشروب ما‪ .‬لهذا المك وّ ن خاص ّية وحي دة‬
‫ً‬ ‫يحتوي هذا التطبيق على مكوّ ن وحيد‬

‫اسمها ‪ name‬تع ّبر عن اسم هذا المش روب‪ .‬انظ ر إلى ش يفرة ‪ Vue.js‬ال تي سأض عها ض من المل ف ‪ app.js‬ال ذي‬

‫توا‪:‬‬ ‫ّ‬
‫المجلد ‪ hsoub-drinks‬الذي أنشأته ًّ‬ ‫سيكون موجودً ا ضمن‬

‫{ ‪Vue.component('drink',‬‬
‫‪template: '#drink-template',‬‬
‫{ ‪props:‬‬
‫{ ‪name:‬‬
‫‪type: String‬‬
‫}‬
‫}‬
‫;)}‬

‫{(‪new Vue‬‬
‫‪el: '#app',‬‬

‫‪83‬‬
Vue.js ‫أساسيات إطار العمل‬ Vue.js ‫التعمق في مكونات‬

data: {
drinks: ["‫ "بابونج‬,"‫ "زهورات‬,"‫ "شاي أخضر‬,"‫ "قهوة‬,"‫]"شاي‬
}
})

‫ بسيط في األسفل يحتوي‬Vue.js ‫ وكائن‬،‫معرف في األعلى‬


ّ ‫ مكوّ ن عادي‬:‫الحظ كم هو بسيط هذا التطبيق‬
.‫ كما هو واضح‬drinks ‫ هذه البيانات موجودة ضمن المصفوفة‬.‫على البيانات التي نريد عرضها على الشاشة‬

ً ‫ وسيكون موج ودً ا‬index.html ‫سأسمه‬


‫أيض ا‬ ّ ‫ التي سأضعها ضمن ملف‬HTML ‫لننتقل اآلن إلى شيفرة‬
ّ
:hsoub-drinks ‫المجلد‬ ‫ضمن‬

<html lang="ar">

<body>
<div class="header">
<span id="logo">‫<مشروبات حسوب‬/span>
</div>

<div id="app" class="container">


<div class="content">
<h1 class="title">‫<المشروبات المتوفرة‬/h1>

<div class="drinks">
<drink v-for="drink in drinks" v-
bind:name="drink"></drink>
</div>
</div>
</div>

<script type="text/x-template" id="drink-template">


<div class="drink">
<div class="description">
<span class="title">
{{name}}
</span>
</div>
</div>
</script>

<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script src="app.js"></script>
<link rel="stylesheet" href="app.css" />

</body>

</html>

84
Vue.js ‫أساسيات إطار العمل‬ Vue.js ‫التعمق في مكونات‬

‫ وبالت الي تولي د‬drinks ‫ في المرور على عناصر المص فوفة‬v-for ‫مر معنا فيما سبق نستخدم حلقة‬
ّ ‫كما‬
َّ
ّ ‫المولدة وال تي‬
‫تمث ل‬ ً ‫ الح ظ‬.‫ كم ا ه و واض ح من الش يفرة الس ابقة‬drink ‫عنص ر المك وّ ن‬
‫أيض ا أنّ العناص ر‬

‫ أرج و أن ال يختل ط علي ك‬.drinks ‫ يحم ل الص نف‬div ‫المشروبات المتوفرة ستكون موج ودة ض من عنص ر‬

‫ وه ذا أمر‬،‫ ألنّ كليهم ا يحمالن نفس االس م‬drink ‫ وبين ص نف التنس يق‬drink ‫األم ر بين اس م المك وّ ن‬

.‫تماما‬
ً ‫جائز‬

ً ‫ ض عه‬app.css ‫ ال ذي سأض عها ض من المل ف‬CSS ‫يرا تنس يقات‬


‫أيض ا ض من‬ ً ‫بقيت أخ‬

:hsoub-drinks ‫المجلد‬

@import url(//fonts.googleapis.com/earlyaccess/notonaskharabic.css);
body {
height: 100vh;
-webkit-font-smoothing: auto;
-moz-osx-font-smoothing: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: 'Noto Naskh Arabic', serif;
background-color: #ccdcdc;
background-repeat: no-repeat;
background-position: 100% 100%
}

span#logo {
font-weight: 700;
color: #eee;
font-size: larger;
letter-spacing: .05em;
padding-left: .5rem;
padding-right: .5rem;
padding-bottom: 1rem;
float: right;
padding-top: 6px;
margin-right: 20px;
}

.header{
background-color:slategray;
width: 80% ;
height: 50px;
margin-left: auto;
margin-right: auto;
}

h1.title {
text-align: center;
font-size: 1.875rem;

85
Vue.js ‫أساسيات إطار العمل‬ Vue.js ‫التعمق في مكونات‬

font-weight: 500;
color: #2d3336
}

h2.subtitle {
margin: 8px auto;
font-size: x-large;
text-align: center;
line-height: 1.5;
max-width: 500px;
color: #5c6162
}

.content {
margin-left: auto;
margin-right: auto;
padding-top: 1.5rem;
padding-bottom: 1.5rem;
width: 620px
}

.drinks {
padding: 0 40px;
margin-bottom: 40px
}

.drinks .drink {
background-color: #fff;
-webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1);
margin-top: 1rem;
margin-bottom: 1rem;
border-radius: .25rem;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
cursor: pointer;
position: relative;
-webkit-transition: all .3s ease;
transition: all .3s ease
}

.drinks .drink, .drinks .drink>.weight {


display: -webkit-box;
display: -ms-flexbox;
display: flex
}

.drinks .drink>.description {
width: 100%;
padding: 1rem;
}

86
Vue.js ‫أساسيات إطار العمل‬ Vue.js ‫التعمق في مكونات‬

.drinks .drink>.description .title {


color: #3d4852;
display: block;
font-weight: 700;
margin-bottom: .25rem;
float: right;
}

.drinks .drink>.description .description {


font-size: .875rem;
font-weight: 500;
color: #8795a1;
line-height: 1.5
}

.drinks .drink>.price {
width: 20%;
color: #09848d;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
padding-top: 1.5rem;
font-family: Crimson Text, serif;
font-weight: 600
}

.drinks .drink>.price .dollar-sign {


font-size: 24px;
font-weight: 700
}

.drinks .drink>.price .number {


font-size: 72px;
line-height: .5
}

.drinks .active-drink, .drinks .drink:hover {


-webkit-box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0
rgba(0, 0, 0, .08);
box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0 rgba(0,
0, 0, .08)
}

.drinks .active-drink:after, .drinks .drink:hover:after {


border-width: 2px;
border-color: #7dacaf;
border-radius: .25rem;
content: "";
position: absolute;
display: block;
top: 0;

87
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫;‪bottom: 0‬‬
‫;‪left: 0‬‬
‫‪right: 0‬‬
‫}‬

‫ً‬
‫جاهزا‪ .‬نستطيع اآلن المتابعة مع موضوعات هذا الفصل‪.‬‬ ‫أصبح التطبيق النموذجي‬

‫‪ 6.2‬إضافة وسائل تنقيح متطورة لتطبيقات ‪Vue.js‬‬


‫ّ‬
‫يوفر مطورو ‪ Vue.js‬أدوات تنقيح متطوّ ره مخصصة لتطبيق ات ‪ Vue.js‬بحيث تس هّ ل حي اة الم برمج إلى‬

‫ح دّ كب ير‪ .‬تظه ر ه ذه األدوات ض من أدوات المط وّ ر ال تي يمكن الوص ول إليه ا بض غط المفت اح ‪.F12‬‬

‫يمكنك زيارة هذه الصفحة لالطالع على كيفية تثبيت هذه األدوات‪.‬‬

‫بعد أن تثبت األدوات السابقة‪ ،‬يمكنك الوص ول إليه ا بض غط المفت اح ‪ F12‬كم ا أش رنا‪ ،‬ثم تبحث عن لس ان‬

‫التبويب ‪ ( Vue.js‬قد تحتاج إلى نقر زر عرض المزيد في حال لم تجد لسان التبويب هذا)‪ .‬انظ ر الش كل الت الي‬

‫لترى كيف تبدو‪.‬‬

‫‪88‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫‪ 6.3‬المكونات المتداخلة‬

‫نحتاج في بعض األحيان إلى جعل المكونات متداخلة فيما بينها‪ ،‬كأن يك ون هن اك مك وّ ن أساس ي (مك وّ ن‬

‫أب) يستخدم مكوّ نات أصغر (مكوّ نات أبناء) ضمنه‪ .‬سنوظف هذا المفهوم ضمن التطبيق النموذجي السابق‪.‬‬

‫سأض يف مكوّ ًن ا جدي دً ا ض من المل ف ‪ app.js‬وسأس ّمه ‪ .drink-selector‬س يلعب ه ذا المك وّ ن دور‬

‫المك وّ ن األساس ي ال ذي يح وي المك وّ ن ‪ drink‬ال ذي س يكون ض منه بش كل مت داخل‪ .‬انظ ر إلى محتوي ات‬

‫الملف ‪ app.js‬بعد إضافة المكوّ ن الجديد إليه‪:‬‬

‫{ ‪Vue.component('drink',‬‬
‫‪template: '#drink-template',‬‬
‫{ ‪props:‬‬
‫{ ‪name:‬‬
‫‪type: String‬‬
‫}‬
‫}‬
‫)}‬

‫{ ‪Vue.component('drink-selector',‬‬
‫‪template: '#drink-selector-template',‬‬
‫{ )(‪data‬‬
‫{ ‪return‬‬
‫]"شاي"‪" ,‬قهوة"‪" ,‬شاي أخضر"‪" ,‬زهورات"‪" ,‬بابونج"[ ‪drinks:‬‬
‫}‬
‫}‬
‫)}‬

‫{(‪new Vue‬‬
‫'‪el: '#app‬‬
‫)}‬

‫الحظ معي كيف نقلت البيانات الخاصة بالتطبيق من كائن ‪ Vue.js‬إلى المكوّ ن ‪ .drink-selector‬لهذا‬
‫ً‬
‫الحقا إن شاء اهلل‪.‬‬ ‫األمر فائدة سنراها‬

‫بالنسبة لشيفرة ‪ HTML‬فاألمر بسيط ً‬


‫أيضا‪ .‬سننشئ القالب ‪ drink-selector-template‬المع ّب ر عن‬

‫مكوّ ننا الجديد‪ .‬وسننقل إليه الشيفرة الخاصة بتوليد قائمة المشروبات‪ .‬انظر محتوى القالب‪:‬‬

‫>"‪<script type="text/x-template" id="drink-selector-template‬‬


‫>"‪<div class="drinks‬‬
‫‪<drink v-for="drink in drinks" v-‬‬
‫>‪bind:name="drink"></drink‬‬
‫>‪</div‬‬
‫>‪</script‬‬

‫‪89‬‬
Vue.js ‫أساسيات إطار العمل‬ Vue.js ‫التعمق في مكونات‬

ً ‫بداًل من الشيفرة التي نقلناها‬


‫ انظ ر كي ف ستص بح‬.>drink-selector< ‫توا يمكنك اس تخدام العنص ر‬

:index.html ‫الشيفرة بعد التعديل األخيرة على الملف‬

<html lang="ar">

<body>
<div class="header">
<span id="logo">‫<مشروبات حسوب‬/span>
</div>

<div id="app" class="container">


<div class="content">
<h1 class="title">‫<المشروبات المتوفرة‬/h1>
<drink-selector></drink-selector>

</div>
</div>

<script type="text/x-template" id="drink-selector-template">


<div class="drinks">
<drink v-for="drink in drinks" :name="drink"></drink>
</div>
</script>

<script type="text/x-template" id="drink-template">


<div class="drink">
<div class="description">
<span class="title">
{{name}}
</span>
</div>
</div>
</script>

<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script src="app.js"></script>
<link rel="stylesheet" href="app.css" />

</body>

</html>

ً
‫ يمكن ك اآلن أن تنشئ‬.‫إذا اس تطعنا اس تخدام مك وّ ن ض من مك وّ ن آخ ر بش كل مت داخل‬

:‫ جديدة بنسخ ولصق السطر التالي بشكل متكرر‬drink-selector ‫عناصر‬

<drink-selector></drink-selector>

90
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫‪ 6.4‬تسجيل المكونات محل ًيا وتسجيلها عىل المستوى العام‬

‫األول هو األسلوب الع ام‪ ،‬وه و‬


‫يمكن تسجيل المكوّ نات ضمن تطبيق ‪ Vue.js‬بأسلوبين مختلفين‪ .‬األسلوب ّ‬

‫األس لوب ال ذي اس تخدمناه ح تى ه ذه اللحظ ة باس تخدام الت ابع ‪ .Vue.component‬واألس لوب اآلخ ر ه و‬

‫سنتعرف عليه في هذه الفقرة‪.‬‬


‫ّ‬ ‫األسلوب المحلي والذي‬

‫جيدً ا‪ ،‬أل ّنه عن دما س تكبر تطبيقات ك وتب دأ‬


‫أمرا ّ‬
‫في الحقيقة ال يُ عتبر تسجيل المكوّ نات على المستوى العام ً‬
‫باستخدام أساليب متقدمة في بناء التطبيقات (كما سيمر معنا في الفصول الالحقة)‪ ،‬سيتم بناء المكوّ نات التي‬

‫سجلتها على المستوى العام وذلك في التطبيق النهائي‪ ،‬حتى ولو لم تستخدمها في ذل ك التط بيق‪ .‬وه ذا يع ني‬

‫زيادة في حجم شيفرة ‪ JavaScript‬التي على المس تخدمين تحميله ا من اإلن ترنت دون فائ دة‪ .‬ه ذا فض اًل عن‬

‫مش اكل من الناحي ة التص ميمية للتط بيق‪ .‬ففي تطبيقن ا األخ ير مثاًل ‪ ،‬لن نس تخدم المك وّ ن ‪ drink‬خ ارج‬

‫المكوّ ن ‪ ،drink-selector‬وبالتالي ال حاجة لتسجيل المكوّ ن ‪ drink‬على المستوى العام‪.‬‬

‫لحل هذه المشكلة‪ ،‬وبالتالي تسجيل المكوّ ن ات بش كل محلي إذا اقتض ى األم ر ذل ك‪ ،‬فيمكنن ا بك ل بس اطة‬

‫متغي ر ع ادي ب دون اس تخدام الت ابع‬


‫ّ‬ ‫تعري ف المك وّ ن على ش كل ك ائن ‪ JavaScript‬وإس ناده إلى‬

‫‪ .Vue.Component‬ثم تس جيله في المك ان ال ذي سنس تخدمه في ه فق ط‪ .‬دعن ا نط ّب ق ه ذه الطريق ة على‬

‫المكوّ ن ‪ drink‬الذي سيصبح تعريفه على النحو التالي‪:‬‬

‫{ = ‪let drink_component‬‬
‫‪template: '#drink-template',‬‬
‫{ ‪props:‬‬
‫{ ‪name:‬‬
‫‪type: String‬‬
‫}‬
‫}‬
‫}‬

‫سيس تخدم ض من المك ون ‪ drink-selector‬ل ذلك سننش ئ قس ًما جدي دً ا ض من‬


‫بم ا أنّ ه ذا المك وّ ن ُ‬
‫المكون ‪ drink-selector‬اسمه ‪ components‬والذي يس مح بتس جيل أي مكوّ ن ات داخلي ة سيس تخدمها‬

‫هذا المكون‪ .‬انظر إلى تعريف المكون ‪ drink-selector‬بعد التعديل‪:‬‬

‫{ ‪Vue.component('drink-selector',‬‬
‫‪template: '#drink-selector-template',‬‬
‫{‪components:‬‬
‫‪drink: drink_component‬‬
‫‪},‬‬
‫{ )(‪data‬‬
‫{ ‪return‬‬

‫‪91‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫]"شاي"‪" ,‬قهوة"‪" ,‬شاي أخضر"‪" ,‬زهورات"‪" ,‬بابونج"[ ‪drinks:‬‬


‫}‬
‫}‬
‫)}‬

‫محلي ا ض من المك وّ ن ‪ drink-selector‬وال يمكن بع د ذل ك‬


‫ّ‬ ‫وبذلك نك ون ق د س ّجلنا المك وّ ن ‪drink‬‬

‫استخدام المكون ‪ drink‬في أي مكان آخر غير المكون ‪.drink-selector‬‬

‫كما يمكن بطبيعة الحال فعل األمر ذاته مع المكوّ ن ‪ drink-selector‬أي تسجيله محليا بدون استخدم‬

‫التابع ‪ Vue.component‬في حال أردنا استخدام هذا المكوّ ن فقط ضمن صفحة محدّ دة ضمن تطبيق ال ويب‪.‬‬

‫سنجر اآلن تعدياًل مماثاًل على المكوّ ن ‪:drink-selector‬‬

‫{ = ‪let drink_selector_component‬‬
‫‪template: '#drink-selector-template',‬‬
‫{‪components:‬‬
‫‪drink: drink_component‬‬
‫‪},‬‬
‫{ )(‪data‬‬
‫{ ‪return‬‬
‫]"شاي"‪" ,‬قهوة"‪" ,‬شاي أخضر"‪" ,‬زهورات"‪" ,‬بابونج"[ ‪drinks:‬‬
‫}‬
‫}‬
‫}‬

‫اآلن‪ ،‬أين تعتقد أ ّنه يجب تسجيل المكوّ ن ‪drink-selector‬؟‬

‫الجواب بكل بساطة‪ ،‬وبما أ ّنه ال يوجد مكوّ ن رئيسي يمكن أن يُ َّ‬
‫عرف المك وّ ن ‪ drink-selector‬ض منه‪،‬‬
‫فسنسجله ضمن كائن ‪ Vue.js‬الخاص بالتطبيق باستخدام القسم ‪ً components‬‬
‫أيضا‪:‬‬

‫{(‪new Vue‬‬
‫‪el: '#app',‬‬
‫{‪components:‬‬
‫‪'drink-selector': drink_selector_component‬‬
‫}‬
‫)}‬

‫الحظ معي أ ّنني قد عرفت اسم المكوّ ن ه ذه الم رة على ش كل نص‪ drink-selector :‬بش كل مختل ف‬

‫عن تعري ف المك وّ ن ‪ .drink‬الس بب في ذل ك أ ّن ني أرغب باالس تمرار باس تخدام الرم ز ‪ -‬ض من اس م المك ون‬

‫تمام ا ب الطبع‪ .‬إذا أردت االطالع على الش كل النه ائي للمل ف ‪ app.js‬بع د‬
‫‪ drink-selector‬وه ذا ج ائز ً‬
‫التعديالت األخيرة‪ ،‬انظر إلى الشيفرة التالية‪:‬‬

‫‪92‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫{ = ‪let drink_component‬‬
‫‪template: '#drink-template',‬‬
‫{ ‪props:‬‬
‫{ ‪name:‬‬
‫‪type: String‬‬
‫}‬
‫}‬
‫}‬

‫{ = ‪let drink_selector_component‬‬
‫‪template: '#drink-selector-template',‬‬
‫{‪components:‬‬
‫‪drink: drink_component‬‬
‫‪},‬‬
‫{ )(‪data‬‬
‫{ ‪return‬‬
‫]"شاي"‪" ,‬قهوة"‪" ,‬شاي أخضر"‪" ,‬زهورات"‪" ,‬بابونج"[ ‪drinks:‬‬
‫}‬
‫}‬
‫}‬

‫{(‪new Vue‬‬
‫‪el: '#app',‬‬
‫{‪components:‬‬
‫‪'drink-selector': drink_selector_component‬‬
‫}‬
‫)}‬

‫‪ 6.5‬تحديد المكون الذي اختاره المستخدم‬

‫قدما في تطبيق المشروبات‪ ،‬حيث س نعمل اآلن على إض افة م يزة تحدي د المش روب ال ذي اخت اره‬
‫لنمضي ً‬
‫ُكس ب‬ ‫ّ‬
‫المتطلب ات البس يطة‪ ،‬حيث سن ِ‬ ‫المس تخدم عن طري ق النق ر علي ه بالف أرة‪ .‬تحت اج ه ذه الم يزة إلى بعض‬

‫عرف ضمن القسم ‪ data‬كما نعلم‪ ،‬لتحديد فيما إذا كان‬ ‫المكوّ ن ‪ drink‬حقاًل جديدً ا اسمه ‪ُ is_selected‬‬
‫سي ّ‬
‫المستخدم قد اختار هذا المشروب أم ال‪ ،‬باإلضافة إلى تزوي ده بت ابع جدي د يس مح باختي ار المش روب ولنس ّمه‬

‫‪ select‬والذي سيحتوي على تعليمة برمجي ة واح دة تجع ل قيم ة الحق ل ‪ is_selected‬مس اوية للقيم ة‬

‫‪ ،true‬أي تع ّب ر عن عملي ة االختي ار‪ .‬انظ ر كي ف س يبدو ش كل المك ون ‪ drink‬بع د إض افة التع ديلين‬

‫السابقين عليه‪:‬‬

‫{ = ‪let drink_component‬‬
‫‪template: '#drink-template',‬‬
‫{ ‪props:‬‬
‫{ ‪name:‬‬
‫‪type: String‬‬
‫}‬

‫‪93‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫‪},‬‬
‫{ )(‪data‬‬
‫{ ‪return‬‬
‫‪is_selected: false‬‬
‫}‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫{ )(‪select‬‬
‫;‪this.is_selected = true‬‬
‫}‬
‫}‬
‫}‬

‫الموجه ‪ v-on:click‬ضمن قالب‬


‫ّ‬ ‫لنجر اآلن التعديالت الالزمة ضمن الملف ‪ index.html‬حيث سنضيف‬

‫المكوّ ن ‪ drink‬لالستجابة لحدث النقر‪ .‬انظر التعديالت التالية على القالب ‪:drink-template‬‬

‫>"‪<script type="text/x-template" id="drink-template‬‬


‫>"‪<div v-on:click="select" class="drink‬‬
‫>"‪<div class="description‬‬
‫>"‪<span class="title‬‬
‫}}‪{{name‬‬
‫>‪</span‬‬
‫>‪</div‬‬
‫>‪</div‬‬
‫>‪</script‬‬

‫ع د إلى المتص ّفح لمعاين ة التغي يرات (ت ذكّر أ ّنن ا نس تعرض المل ف ‪ index.html‬ض من‬

‫الخادوم ‪ .)Live Server‬جرب أن تنقر على المشروبين‪" :‬شاي" و "ش اي أخض ر"‪ ،‬ثم انتق ل إلى ناف ذة أدوات‬

‫المطوّ ر بضغط ‪ ،F12‬ومن هناك انتقل إلى لسان التبويب ‪ .Vue‬انشر العقد في حال احتجت ذلك‪ ،‬ستحصل على‬

‫شكل شبيه بما يلي بالنسبة للمشروب األول "شاي"‪:‬‬

‫‪94‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫جرب أن تعاين حالة المشروب التالي "قهوة" ستجد أن قيمة الحق ل ‪ is_selected‬ل ه تس اوي ‪false‬‬

‫أل ّنن ا لم ننق ر على القه وة‪ .‬ج رب اآلن أن تع اين حال ة المش روب الت الي "ش اي أخض ر" ‪ ،‬س تالحظ أنّ‬
‫ّ‬
‫متوق ع من ه ح تى اآلن‪.‬‬ ‫حاليا يعم ل كم ا ه و‬
‫ً‬ ‫قيمة ‪ is_selected‬في هذه الحالة هي ‪ .true‬أي أنّ التطبيق‬

‫بقي أن نض يف بعض الت أثيرات البص رية البس يطة لإلش ارة إلى أنّ المش روب ق د اخت ير من قب ل المس تخدم‪.‬‬

‫الموج ه ‪ v-bind:class‬إلض افة تنس يق ‪ CSS‬وذل ك ض من الق الب ‪ drink-template‬على‬


‫ّ‬ ‫سأس تخدم‬

‫النحو التالي‪:‬‬

‫>"‪<script type="text/x-template" id="drink-template‬‬


‫‪<div v-on:click="select" class="drink" v-bind:class="{'active-‬‬
‫>"}‪drink': is_selected‬‬
‫>"‪<div class="description‬‬
‫>"‪<span class="title‬‬
‫}}‪{{name‬‬
‫>‪</span‬‬
‫>‪</div‬‬
‫>‪</div‬‬
‫>‪</script‬‬

‫س ُيط ّبق تنس يق ‪ CSS‬المس مى '‪ 'active-drink‬فق ط عن دما تك ون قيم ة الحق ل ‪is_selected‬‬

‫تس اوي ‪ true‬أي عن دما يخت ار المس تخدم المش روب الح الي‪ .‬وال حاج ة بطبيع ة الح ال الس تخدام عالم ة‬

‫االقتباس المفردة في حال لم يحتوي اسم الصنف على رمز مثل ‪ .-‬بقي أن نضيف تنسيق ‪ CSS‬نفس ه إلى مل ف‬

‫التنسيقات ‪ .app.css‬أضف الصنفين التاليين إلى ذلك الملف‪:‬‬

‫{ ‪.drinks .active-drink, .drinks .drink:hover‬‬


‫;‪background-color: lightgray‬‬
‫‪-webkit-box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0‬‬
‫;)‪rgba(0, 0, 0, .08‬‬
‫‪box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0 rgba(0,‬‬
‫)‪0, 0, .08‬‬
‫}‬

‫{ ‪.drinks .active-drink:after, .drinks .drink:hover:after‬‬


‫;‪border-width: 2px‬‬
‫;‪border-color: #7dacaf‬‬
‫;‪border-radius: .25rem‬‬
‫;"" ‪content:‬‬
‫;‪position: absolute‬‬
‫;‪display: block‬‬
‫;‪top: 0‬‬
‫;‪bottom: 0‬‬
‫;‪left: 0‬‬
‫‪right: 0‬‬
‫}‬

‫‪95‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫عد إلى المتصفح واختر المشروب األول والثالث مرة أخرى لتحصل على شكل شبيه بما يلي‪:‬‬

‫تمت المهمة بنجاح! ولكن‪ ،‬ماذا لو أردنا اختيار مشروب واح د فق ط من القائم ة؟ س نحل ه ذه المش كلة‬
‫لقد ّ‬

‫مخصصة‪.‬‬
‫ّ‬ ‫ّ‬
‫سنتعلم كيفية التخاطب بين المكوّ نات المختلفة باستخدام أحداث‬ ‫في الفقرة التالية حيث‬

‫‪ 6.6‬التخاطب بني المكونات باستخدام أحداث مخصصة‬

‫برزت الحاجة في الفقرة السابقة إلى اختي ار مش روب واح د فق ط من قائم ة المش روبات المتاح ة‪ .‬أي أ ّنن ا‬

‫معينة تسمح باختيار مشروب واحد فقط‪ ،‬بحيث تعمل هذه اآللي ة على إلغ اء أي اختي ار س ابق‬
‫بحاجة إلى آلية ّ‬
‫أخيرا‪.‬‬
‫ً‬ ‫لمشروب ما ( في حال وجوده) ضمن القائمة‪ ،‬واإلبقاء على المشروب الذي اختاره المستخدم‬

‫يمكن تحقيق ه ذا األم ر بالتخ اطب بين المك وّ ن االبن ‪ drink‬والمك وّ ن األب ‪ drink-selector‬حيث‬

‫سنستخدم المكوّ ن األب للمساعدة في هذا األمر‪.‬‬

‫المخصصة التي سنتحدث عنها بع د قلي ل‪ ،‬حيث س نبلغ‬


‫ّ‬ ‫يمكن التخاطب بين المكونات باستخدام األحداث‬

‫المكوّ ن األب ‪ drink-selector‬بأن مشروبًا ما قد اختاره المستخدم‪ ،‬فيعمل عندئذ المك ون األب على إلغ اء‬

‫اختيار أي مشروب سابق يمكن أن يكون قد اختير من قبل‪ ،‬باستثناء المشروب الحالي‪.‬‬

‫قبل المتابعة‪ ،‬توقف عن الق راءة قلياًل ‪ ،‬حض ر كو ًب ا من الش اي (أو القه وة) ‪ ،‬ثم ع د إلى هن ا من جدي د‪ ،‬ألنّ‬

‫الشرح التالي يحتاج إلى المزيد من التركيز‪.‬‬

‫‪96‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫سنبدأ من الملف ‪ app.js‬حيث س نجري بعض التع ديالت على المك ونين ‪ drink‬و ‪.drink-selector‬‬

‫بالنسبة للمكون ‪ drink‬سأجري التعديالت التالية‪:‬‬

‫استخدمنا في المثال في الفقرة السابقة الحق ل ‪ is_selected‬لإلش ارة إلى ك ون المش روب الح الي‬ ‫•‬

‫قد اختير أم ال‪ .‬سأحول ه ذا الحق ل إلى خاص ية محس وبة (‪ )Computed Property‬باإلض افة إلى‬

‫أ ّنني سأحذف قسم ‪ data‬بشكل كامل‪ ،‬وسنرى سبب ذلك باإلضافة بعد قليل‪.‬‬

‫ألول م رة الت ابع المض ّمن ‪ $emit‬ض من الت ابع ‪( select‬ب داًل من التعليم ة‬
‫كم ا سأس تخدم ّ‬ ‫•‬

‫‪ )this.is_selected = true‬لكي ُنص در الح دث ‪ drink_selected_event‬وذل ك لكي‬

‫ان‬ ‫ّ‬
‫نبلغ المكون األب بأن المشروب الحالي ق د اخت ير‪ ،‬وذل ك ب أن أم ّرر اس م ه ذا المش روب كوس يط ث ٍ‬
‫للتابع ‪.emit$‬‬

‫سأضيف ً‬
‫أيضا خاصية جدي دة اس مها ‪ selectedDrink‬إلى المك ون ‪ drink‬ض من القس م ‪props‬‬ ‫•‬

‫وذل ك لكي أس مح للمك ون األب فيم ا بع د‪ ،‬بتمري ر اس م المش روب (مك ون ‪ )drink‬ال ذي اخت اره‬

‫حاليا إلى هذا المكون‪.‬‬


‫ً‬ ‫المستخدم‬

‫تأمل الشيفرة البرمجية الجديدة الخاصة بالمكون ‪:drink‬‬


‫ّ‬

‫{ = ‪let drink_component‬‬
‫‪template: '#drink-template',‬‬
‫{ ‪props:‬‬
‫{ ‪name:‬‬
‫‪type: String‬‬
‫‪},‬‬
‫{‪selectedDrink:‬‬
‫‪type: String‬‬
‫}‬
‫‪},‬‬
‫{‪computed:‬‬
‫{)(‪is_selected‬‬
‫;‪return this.selectedDrink === this.name‬‬
‫}‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫{ )(‪select‬‬
‫)‪this.$emit('drink_selected_event', this.name‬‬
‫}‬
‫}‬
‫}‬

‫‪97‬‬
Vue.js ‫أساسيات إطار العمل‬ Vue.js ‫التعمق في مكونات‬

ً ‫ فسأجري عليه‬drink-selector ‫أما بالنسبة للمكوّ ن‬


:‫أيضا التعديالت التالية‬ ّ

‫ وال ذي‬drink_selected_handler ‫ لكي أس تطيع وض ع الت ابع‬methods ‫وضعت ضمنه القسم‬ •

‫ ال ذي سأص دره (باس تخدام الت ابع المض ّمن‬drink_selected_event ‫معالج ا للح دث‬
ً ‫س يكون‬

.‫ كما رأينا قبل قليل‬drink ‫ في المكوّ ن‬select ‫)ضمن التابع‬$emit

‫ وهو الذي سيحتفظ باسم المكوّ ن‬data ‫ ضمن القسم‬current_drink ‫أضفت حقاًل جديدً ا أسميته‬ •

.‫توا‬
ًّ ‫(المشروب) الذي اختاره المستخدم‬

:drink-selector ‫إليك الشيفرة البرمجية الجديدة الخاصة بالمكون‬

let drink_selector_component = {
template: '#drink-selector-template',
components: {
drink: drink_component
},
data() {
return {
drinks: ["‫ "بابونج‬,"‫ "زهورات‬,"‫ "شاي أخضر‬,"‫ "قهوة‬,"‫]"شاي‬,
current_drink: null
}
},
methods:{
drink_selected_handler(drink_name){
this.current_drink = drink_name;
}
}
}

:drink-selector ‫إليك اآلن التعديالت التي حدثت ضمن قالب المكوّ ن‬

<script type="text/x-template" id="drink-selector-template">


<div class="drinks">
<drink v-for="drink in drinks" v-bind:name="drink"
v-on:drink_selected_event="drink_selected_handler"
v-bind:selectedDrink="current_drink"></drink>
</div>
</script>

:‫أريد هنا التركيز على أمرين هما في صلب الموضوع‬

‫مبيتً ا‬
ّ ‫الموج ه ليس‬
ّ ‫ ه ذا‬.v-on:drink_selected_event ‫للموج ه‬
ّ ‫األول ه و اس تخدامي‬
ّ ‫األمر‬ •

‫ وتمري ري‬$emit ‫موج ه جدي د ُمح دَ ث بس بب اس تخدامي للت ابع‬


ّ ‫ إ ّنم ا ه و‬.‫باألص ل‬

98
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫مخصص ة باس تخدام الت ابع‬


‫ّ‬ ‫موجه ات‬
‫ّ‬ ‫للقيم ة '‪ 'drinkselectedevent‬ل ه‪ً .‬‬
‫إذا يمكن إنش اء‬

‫المبيت ‪.$emit‬‬
‫ّ‬

‫الموجه"‪ v-bind:selectedDrink="current_drink‬ال ذي يرب ط قيم ة‬


‫ّ‬ ‫األم ر الث اني ه و‬ ‫•‬

‫الموجه االبن‪.‬‬
‫ّ‬ ‫الموجه األب‪ ،‬مع الخاصية ‪ selectedDrink‬من‬
‫ّ‬ ‫الحقل ‪ current_drink‬من‬

‫فسيس تدعى في البداي ة‬


‫ُ‬ ‫فالذي يحدث بكل بساطة‪ ،‬هو أ ّنه عندما يختار المستخدم مشروبًا ما من القائم ة‪،‬‬
‫التابع ‪ select‬من المكون االبن‪ .‬ضمن هذا التابع ّ‬
‫لتنفذ التعليمة البرمجية‪:‬‬

‫)‪this.$emit('drink_selected_event', this.name‬‬

‫التي تعمل على اس تدعاء ح دث مخص ص للتخ اطب م ع المك وّ ن األب‪ ،‬حيث يق ول المك وّ ن االبن للمك وّ ن‬
‫علما أنّ اسم المك وّ ن االبن (اس م المش روب) س ّ‬
‫يمرر ض من الوس يط الث اني من‬ ‫تم اختياري!"‪ً ،‬‬
‫األب‪" :‬انظر لقد ّ‬

‫التابع ‪ $emit‬كما أوضحنا قبل قليل‪.‬‬

‫الموجه ‪ "v-on:drink_selected_event="drink_selected_handler‬ضمن‬
‫ّ‬ ‫بسبب وجود‬

‫وسيس تدعى الت ابع ‪ drink_selected_handler‬من المك وّ ن‬


‫ُ‬ ‫قالب المك وّ ن األب‪ ،‬س ُيلتَ قط ه ذا الح دث‪،‬‬

‫األب‪ ،‬حيث تعمل تعليمة بسيطة ضمنه على إسناد اسم المشروب (المكون االبن) الذي اختاره المس تخدم ض من‬

‫الحق ل ‪ current_drink‬وبه ذه الطريق ة يع رف المك وّ ن األب من ه و المش روب الح الي ال ذي‬

‫اختاره المستخدم‪.‬‬

‫س تؤدي ه ذه المعرف ة‪ ،‬وبس بب طبيع ة ‪ Vue.js‬إلى تح ديث ق الب المك وّ ن بكامل ه‪ ،‬وبالت الي س ّ‬
‫تنفذ‬

‫يمرر اس م المك وّ ن (المش روب) ال ذي اخت اره المس تخدم إلى‬


‫حلقة ‪ v-for‬مرة أخرى‪ ،‬ولكن في هذه الحالة س ّ‬

‫جميع المشروبات الموجودة ضمن المكوّ ن األب عن طريق الخاصية ‪ selectedDrink‬وبالت الي س يعرف ك ل‬

‫حالي ا‪ .‬وبس بب وج ود الخاص ية المحس وبة‬


‫ً‬ ‫مكوّ ن ابن من المكوّ ن االبن "المحظوظ" ال ذي اخت اره المس تخدم‬
‫ّ‬
‫ستنفذ التعليمة البرمجية الموجودة ضمنها‪:‬‬ ‫‪ is_selected‬في المكون االبن‪،‬‬

‫;‪return this.selectedDrink === this.name‬‬

‫هذه التعليمة بسيطة للغاية‪ ،‬وهي ُترجع قيمة من النوع المنطقي ‪ ture‬إذا كان اسم المكوّ ن الحالي يط ابق‬

‫اسم المكوّ ن "المحظوظ" الذي اختاره المستخدم‪ .‬أو ُترجع القيمة ‪ false‬إذا لم تتحقق هذه المطابقة‪.‬‬

‫الموج ه‬
‫ّ‬ ‫زبدة القول‪ ،‬فإنّ القيمة التي سترجعها الخاصية المحسوبة ‪ is_selected‬هي التي ستُ سند إلى‬

‫}‪ "v-bind:class="{'active-drink': is_selected‬وبالتالي يظهر هذا المشروب كم ا ل و أ ّن ه‬

‫تم اختياره بتطبيق تنسيق ‪ CSS‬المناسب أو يظهر بشكل عادي‪.‬‬

‫‪99‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعمق في مكونات ‪Vue.js‬‬

‫نستنتج مما سبق‪ ،‬أ ّنه يمكن للمكوّ ن األب أن يرسل رسائل إلى المكوّ ن االبن الموجود ضمنه‪ ،‬عن طريق‬

‫الخصائص التي نعرفها ضمن القسم ‪ props‬في المكون االبن‪ ،‬التي ُتعتبر مستقبالت للمكون االبن تسمح‬

‫أما عندما يريد المكون االبن مخاطبة المكون األب‪ ،‬فيمكن ذلك‬
‫له بالتقاط الرسائل من خارجه بشكل عام‪ّ .‬‬
‫مخصصة باستخدام التابع ‪ $emit‬كما أوضحنا في المثال األخير‪.‬‬
‫ّ‬ ‫عن طريق أحداث‬

‫جرب التطبيق بعد التعديالت األخيرة‪ .‬الحظ كيف أصبح يسمح باختيار مشروب واحد فقط‪.‬‬

‫‪ 6.7‬ختام الفصل‬

‫لقد تناولنا في هذا الفصل الكثير من الموضوعات المهمة حول المكوّ نات‪ ،‬وأعتق د أ ّن ه أص بح من الض روري‬

‫االنتقال إلى مستوى أعلى‪ ،‬عن طريق بناء تطبيقات بأساليب احترافية توفره ا ‪ Vue.js‬لن ا‪ .‬س نعمل في الفص ل‬

‫القادم إن شاء اهلل على االطالع على كيفية فصل المكوّ ن ات ض من ملف ات مس تقلة تحت وي الواح دة منه ا على‬
‫ّ‬
‫المتعلقة بالمكوّ ن مما يس اهم بتنظيم الش يفرة البرمجي ة إلى ح دّ كب ير‪ .‬باإلض افة إلى االطالع‬ ‫جميع التفاصيل‬

‫على تقنية بناء تطبيقات ‪ Vue.js‬بشكل مختلف عن طريق ‪ .Vue.js CLI‬ح تى الفص ل الق ادم أرج و أن تكون وا‬

‫بأفضل حال!‬

‫‪100‬‬
‫‪ .7‬إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫لماذا ‪Vue CLI‬؟‬ ‫•‬

‫إعداد ‪Vue CLI‬‬ ‫•‬

‫إنشاء مشروع جديد باستخدام ‪Vue CLI‬‬ ‫•‬

‫نظرة عامة على هيكل المشروع‬ ‫•‬

‫تعديل تطبيق مشروبات حسوب حسب األسلوب الجديد‬ ‫•‬

‫وسنتعرف ً‬
‫أيض ا على كيفي ة إع داد ‪Vue CLI‬‬ ‫ّ‬ ‫سنتعرف في هذا الفصل على ‪ Vue CLI‬ولماذا نحتاج إليه‪.‬‬
‫ّ‬
‫بالتفصيل‪ ،‬ثم س نعمل على موائم ة تط بيق "مش روبات حس وب" ال ذي بنين اه في الفص ل الس ابق لكي يتوافق‬

‫مع ‪.Vue CLI‬‬


‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫‪ 7.1‬لماذا ‪Vue CLI‬؟‬

‫بدأنا أول فصل في الكتاب بكتابة التطبيقات التي نحتاج لها ض من موق ع ‪ ،JSFiddle‬ثم طورن ا عملن ا ب أن‬

‫محليا باستخدامه‪،‬‬
‫ًّ‬ ‫محرر الشيفرة البرمجية ‪ Visual Studio Code‬بحيث أصبحنا نكتب تطبيقاتنا‬
‫ّ‬ ‫استخدمنا‬

‫ثم نجرب هذه التطبيقات عن طريق اإلضافة ‪.Live Server‬‬

‫جيد في الواق ع ولكن ق د يك ون في بعض األحي ان غ ير ك اف‪ .‬وخاص ًة إذا ب دأت بكتاب ة‬
‫السيناريو األخير ّ‬
‫تطبيقات أكبر وأكثر تعقيدً ا باستخدام المكوّ نات التي تكتبها أنت والتي تحتاج إليها في تطبيقاتك‪.‬‬

‫المشكلة التي ستواجهها هي التداخل بين الشيفرة الخاصة بالمكوّ نات مع الشيفرة الخاصة بالتطبيق‪ ،‬وه ذا‬

‫يحدث على مستوى شيفرة ‪ JavaScript‬و شيفرة ‪ HTML‬وحتى تنسيقات ‪ .CSS‬في ح ال التطبيق ات الكب يرة‬

‫سيكون هذا التداخل مزعج وأكثر عرضة لألخطاء‪ .‬لذلك فمن الحكمة أحيا ًنا االنتقال إلى مستوى أعلى في بن اء‬

‫تطبيقات عملية وواقعية باستخدام أدوات تطوير فعالة ومناسبة‪.‬‬

‫موج ه األوام ر الخ اص بـ‬


‫ّ‬ ‫بغي ة تحقي ق ه ذا اله دف‪ ،‬سنس تخدم في ه ذا الفص ل ‪ Vue CLI‬وهي واجه ة‬

‫برمجية تسمح لنا ببناء الهيكل األساسي لتطبيق ‪ Vue.js‬تمهي دً ا لكتاب ة‬


‫ّ‬ ‫‪ .Vue.js‬باختصار‪ ،‬هي عبارة عن حزمة‬

‫الشيفرة‪ ،‬باإلضافة إلى تزويدنا بخادوم تطوير بسيط (مختلف عن الخادوم ‪ )Live Server‬يسمح لن ا بمحاك اة‬

‫الظروف الفعلية التي سيعمل ض منها ه ذا التط بيق‪ .‬توج د ع دة ق والب ُمتاح ة يس تطيع ‪ Vue.js‬تولي دها ل ك‪،‬‬

‫سنستخدم أبسطها في هذا الفصل‪.‬‬

‫‪ 7.2‬إعداد ‪Vue CLI‬‬

‫نستطيع تثبيت ‪ Vue CLI‬على مختلف أنواع أنظمة التشغيل األساسية ( ‪)Windows, Linux, Mac OS‬‬

‫بنفس األسلوب تقري ًبا‪ .‬سأركّز هنا على نظام التشغيل ‪.Windows‬‬

‫‪ 7.2.1‬تثبيت ‪Node.js‬‬

‫رغم أ ّنن ا س نحتاج إلى ‪ Node.js‬إاّل أ ّنن ا لن نكتب أي ش يفرة باس تخدامه حالي ا‪ ،‬إنم ا سنس تخدم م دير‬

‫الحزم ‪ npm‬وهو ضروري لتثبيت ‪ ،Vue CLI‬باإلضافة إلى احتواءه على خادوم التطوير الذي تحدثنا عن ه قب ل‬

‫قليل‪ ،‬والذي سيستضيف تطبيقاتنا أثناء تطويرها‪.‬‬

‫ستحتاج إلى ‪ Node.js‬وتأكد أ َّنه مثبت لديك‪ ،‬أو زر الموقع الخاص بـ ‪ِّ Node.js‬‬
‫ونزل اإلصدار األخير من ه‬

‫(يفضل استعمال اإلصدار ‪ LTS‬المستقر طويل الدعم ولكن يمكنك تنزيل أحدث إصدار ‪.)Current‬‬

‫‪102‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫بعد تنزيل برنامج التث بيت‪ ،‬ش ّغل ه ذا البرن امج (س تحتاج إلى ص الحيات م دير النظ ام)‪ ،‬واتب ع خط وات‬

‫بيا‪ .‬بع د االنته اء يمكن ك‬


‫يرا نس ً‬
‫التثبيت مع ترك الخيارات االفتراضية كما هي‪ .‬ستأخذ عملية التثبيت زم ًن ا قص ً‬
‫االنتقال إلى الخطوة التالية‪.‬‬

‫‪ 7.2.2‬تثبيت ‪Vue CLI‬‬

‫موجه األوامر في ‪( Windows‬يمكنك ذلك من خالل ضغط مفتاح الويندوز مع المفت اح ‪ ،R‬ثم كتاب ة‬
‫ّ‬ ‫ّ‬
‫شغل‬

‫األمر ‪ cmd‬مباشر ًة‪ ،‬ونقر زر موافق ‪ .)OK‬بعد ذلك اكتب األمر التالي‪:‬‬

‫‪npm install -g @vue/cli‬‬

‫موج ه‬
‫ّ‬ ‫بيا‪ .‬بع د االنته اء‪ ،‬س يعود‬
‫قصيرا نس ً‬
‫ً‬ ‫ستبدأ عندها عملية تثبيت ‪ Vue CLI‬والتي ستأخذ ً‬
‫أيضا وقتً ا‬

‫األوامر إلى حالته الطبيعية‪.‬‬

‫جاهزا لالستخدام‪ ،‬حيث يمكنك الب دء بتنفي ذ األم ر ‪ vue‬مباش ر ًة ض من‬


‫ً‬ ‫عند هذه النقطة أصبح ‪Vue CLI‬‬

‫متاحا بعد تثبيته باستخدام ‪.npm‬‬


‫ً‬ ‫موجه األوامر ألنه أصبح‬
‫ّ‬

‫موجه أوامر جديدة حتى تستطيع‬


‫ّ‬ ‫قد تحتاج إلى إغالق نافذة موجه األوامر الحالية‪ ،‬وفتح نافذة‬

‫استخدام األمر ‪.vue‬‬

‫‪ 7.2.3‬تثبيت ‪Git‬‬

‫سنحتاج ً‬
‫أيضا إلى تطبيق إدارة اإلصدار ‪ .Git‬يمكنك تحميله من هذا الرابط مع اختيار نسخة وين دوز‪ .‬بع د‬

‫نص ب التط بيق (س تحتاج إلى ص الحيات م دير النظ ام)‪ .‬ات رك الخي ارات االفتراض ية كم ا هي‬
‫تنزي ل المل ف‪ّ ،‬‬

‫باس تثناء الشاش ة ال تي تطلب من ك فيه ا تحدي د الطرفي ة االفتراض ية للـ ‪ .Git‬اخ تر الخي ار‬

‫الثاني ‪ .Use Windows' default console window‬ثم تابع بنقر الزر ‪ Next‬حتى تصل للنهاية‪.‬‬

‫‪103‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫موجه األوامر (ناف ذة جدي دة) واكتب األم ر الت الي لكي نتأكّ د من‬
‫ّ‬ ‫بعد االنتهاء من تثبيت ‪ .Git‬افتح نافذة‬

‫تمت بشكل صحيح‪:‬‬


‫أنّ عملية التثبيت قد ّ‬

‫‪git --version‬‬

‫ستحصل على إصدار النسخة الحالي إذا جرت األمور بشكل سليم‪.‬‬

‫‪ 7.3‬إنشاء مرشوع جديد باستخدام ‪Vue CLI‬‬

‫اآلن وبعد أن انتهينا من إعداد ‪ Vue CLI‬أصبح بإمكاننا الشروع باستخدامه‪ .‬سنبدأ بإنشاء مشروع جدي د‪.‬‬

‫وبما أ ّنن ا س نعمل على نق ل تط بيق "مش روبات حس وب" إلى ‪ Vue CLI‬بع د قلي ل‪ ،‬ل ذلك فسنس مي المش روع‬

‫الجديد الذي سنعمل على إنشائه اآلن باالسم ‪.hsoub-drinks-cli‬‬

‫موجه األوامر‪:‬‬
‫ّ‬ ‫لنبدأ اآلن! اكتب األمر التالي ضمن‬

‫‪vue create hsoub-drinks-cli‬‬

‫من السطر السابق‪ vue :‬هو األمر الخ اص بتنفي ذ ‪ Vue CLI‬كم ا أش رنا‪ ،‬الوس يط ‪ create‬يش ير كم ا ه و‬

‫أم ا ‪ hsoub-drinks-cli‬فه و اس م المش روع‪ ،‬عن د تنفي ذ األم ر الس ابق‬


‫واضح إلى إنشاء مش روع جدي د‪ّ ،‬‬
‫ّ‬
‫مجلد يحمل نفس اسم المش روع‪ ،‬وذل ك في نفس ال دليل الح الي‬ ‫سيعمل ‪ Vue CLI‬على إنشاء المشروع ضمن‬

‫‪104‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫الذي ّ‬
‫ننفذ فيه األمر السابق‪ .‬اضغط المفتاح ‪ Enter‬لتنفيذ األمر ‪ ،‬سيطلب منك ‪ Vue CLI‬المزيد من المعلوم ات‬

‫قبل أن يُ نشأ المشروع‪.‬‬

‫من الشكل السابق‪ ،‬يسأل ‪ Vue CLI‬عن نوع القالب الذي نريد استخدمه‪ ،‬توج د العدي د من الق والب‪ ،‬حيث‬

‫يمكنك ضغط مفتاح السهم السفلي من لوحة المفاتيح الختيار ‪ Manually select features‬واالطالع‬

‫‪default‬‬ ‫على الق والب الج اهزة مس ً‬


‫بقا‪ .‬لمش روعنا البس يط‪ ،‬س أختار الق الب االفتراض ي ‪(babel,‬‬

‫)‪ ،eslint‬أي اضغط على المفتاح ‪ Enter‬فحسب‪.‬‬

‫سيستغرق ذلك بعض الوقت لكي ُت ّ‬


‫حمل الملفات المطلوب ة ويُ ّ‬
‫حض ر المش روع الجدي د لالس تخدام‪ .‬ال تقل ق‬
‫ّ‬
‫ستنزل من اإلنترنت (رغم أنّ المشروع بسيط)‪.‬‬ ‫من عدد الملفات الكبير التي‬

‫في بعض األحيان القليلة‪ ،‬يمكن أن يحدث جمود في عملية إنشاء المشروع لسبب أو آلخر‪ .‬إذا شعرت أنّ‬

‫هذه العملية قد استغرقت وقتً ا طوياًل غير منطقي‪ ،‬أو أنّ عملية التحميل تقف عند حزمة محدّ دة وال‬

‫موجه األوامر‪،‬‬
‫ّ‬ ‫تنتقل إلى حزمة تالية‪ ،‬عندها يمكنك ضغط ‪ Ctrl + C‬إليقاف ‪ ،Vue CLI‬ثم أغلق نافذة‬

‫وكرر العملية‬
‫توا‪ ،‬وأعد تشغيل الحاسوب ّ‬ ‫ّ‬
‫المجلد ‪ hsoub-drinks-cli‬الذي ُأ نشأ ًّ‬ ‫ثم احذف‬

‫من جديد‪.‬‬

‫ّ‬
‫المجلد ‪ hsoub-drinks-cli‬باستخدام األمر التالي‪:‬‬ ‫بعد االنتهاء من إنشاء المشروع‪ .‬ادخل إلى‬

‫‪cd hsoub-drinks-cli‬‬

‫ثم ّ‬
‫نف ذ األمر التالي لتشغيل خادوم التطوير البسيط الذي يأتي مع ‪:npm‬‬

‫‪npm run serve‬‬

‫‪105‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫سيؤدي ذلك إلى تشغيل هذا الخادوم على المنفذ االفتراض ي ‪ 8080‬ليعم ل على تخ ديم ملف ات المش روع‬

‫‪ hsoub-drinks-cli‬الذي أنشأناه ت ًّوا‪ .‬من الممكن أن ُتفتَ ح ناف ذة جدي دة من المتص فح االفتراض ي ل ديك‬

‫بشكل تلقائي بحيث تعرض مباشرة الصفحة الرئيسية االفتراضية‪ .‬أو يمكنك أن تفعل أنت ذلك بأن تفتح ناف ذة‬

‫( أو لسان تبويب) جديدة من المتصفح لديك وتنتقل إلى العنوان الت الي‪ .http://localhost:8080 :‬ستحص ل‬

‫على شكل شبيه بما يلي‪:‬‬

‫الحظ أنه ممن الممكن في بعض األحيان أن يظهر لك تنبي ه أم ني من نظ ام التش غيل‪ ،‬يطلب من ك الس ماح‬

‫لخادوم التطوير بالعمل‪ .‬كما في الشكل التالي‪:‬‬

‫‪106‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫انقر الزر ‪ Allow access‬للسماح بذلك‪.‬‬

‫‪ 7.4‬نظرة عامة عىل هيكل المرشوع‬


‫ّ‬
‫مجلد المش روع ال ذي أنش أناه ت ًّوا‪ .‬يمكن ك ذل ك باختي ار‬ ‫انتقل اآلن إلى ‪ Visual Studio Code‬ثم افتح‬

‫مزدوج ا على ه ذا‬


‫ً‬ ‫ّ‬
‫المجلد الذي أنشأت في ه المش روع‪ .‬انق ر نق ًرا‬ ‫األمر ‪ Open Folder >- File‬ثم انتقل إلى‬
‫ّ‬
‫المجلد لكي تدخل ضمنه‪ ،‬ثم اختر ‪.Select Folder‬‬

‫سيظهر المشروع ضمن نافذة المستكشف في القسم األيسر من الشاشة بشكل شبيه بما يلي‪:‬‬

‫‪107‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫المهم ة وال تي س تتعامل معه ا بش كل متك ّرر‪ .‬انظر‬


‫ّ‬ ‫عام ة عن بعض الملف ات‬
‫س أتحدث هن ا بص ورة ّ‬
‫الجدول التالي‪:‬‬

‫الوصف‬ ‫الملف أو المجلد‬

‫عبارة عن ملف ‪ HTML‬عادي يرسله الخادوم إلى متصفح‬


‫‪public/index.html‬‬
‫الويب عند زيارة الموقع‪ localhost:8080 :‬من المتصفح‪.‬‬

‫يحتوي هذا المجلد على ملفات الشيفرة البرمجية‪ ،‬باإلضافة‬


‫‪src‬‬
‫إلى أصول التطبيق من صور وتنسيقات وغيرها‪.‬‬

‫هذا ملف ‪ JavaScript‬وهو مسؤول عن إعدادات تطبيق‬

‫‪ .Vue JS‬كما يحتوي هذا الملف على أيّ ة حزم من طرف‬ ‫‪src/main.js‬‬
‫ثالث يمكن أن يستخدمها التطبيق‬

‫عموما‪ ،‬هي الملفات التي ُتوضع‬


‫ً‬ ‫الملفات ذات االمتداد ‪vue‬‬

‫ضمنها المكوّ نات (‪ ،)Components‬حيث سنط ّبق مبدأ‬ ‫‪src/App.vue‬‬


‫فصل المكوّ نات بشكل كامل عن ملفات ‪ HTML‬العادية‪.‬‬

‫يحتوي على أصول (‪ )assets‬الموقع مثل الصور وشعار‬


‫‪src/assets‬‬
‫الموقع وملفات التنسيق وغيرها‪.‬‬

‫‪108‬‬
Vue.js ‫أساسيات إطار العمل‬ Vue CLI ‫إنشاء مشاريع باستخدام‬

ّ
.‫المجلد الخاص بالمكوّ نات التي سنكتبها للتطبيق‬ components

‫ لن نحتاج إلى‬،‫ يحتوي على مكوّ ن تجريبي بسيط‬vue ‫ملف‬

‫ حيث سنستبدل به المكوّ نات الخاصة‬.‫هذا الملف بالطبع‬ components/HelloWorld.vue


"‫بتطبيق "مشروبات حسوب‬

ً ‫ ثم أنش ئ‬،Delete ‫ باختياره من ناف ذة المستكش ف ثم ض غط ال زر‬HelloWorld.vue ‫احذف الملف‬


‫ملف ا‬

‫ س ّم المل ف الجدي د‬.New File ‫ بالنقر بزر الف أرة األيمن ثم اختي ار األم ر‬Components ‫جديدً ا ضمن المجلد‬

ّ
.)‫الممثل لمشروب محدّ د (راجع الفصل السابق‬ ‫ سيحوي هذا الملف المكوّ ن‬.drink.vue ‫باالسم‬

:‫ الجديد‬drink.vue ‫انسخ الشيفرة البرمجية التالية إلى الملف‬

<template>
<div v-on:click="select" class="drink" v-bind:class="{'active-
drink': is_selected}">
<div class="description">
<span class="title">{{name}}</span>
</div>
</div>
</template>

<script>
export default {
name: "drink",
props: {
name: {
type: String
},
selectedDrink: {
type: String
}
},
computed: {
is_selected() {
return this.selectedDrink === this.name;
}
},
methods: {
select() {
this.$emit("drink_selected_event", this.name);
}
}
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->

109
Vue.js ‫أساسيات إطار العمل‬ Vue CLI ‫إنشاء مشاريع باستخدام‬

<style scoped>
.drinks {
padding: 0 40px;
margin-bottom: 40px
}

.drinks .drink {
background-color: #fff;
-webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1);
margin-top: 1rem;
margin-bottom: 1rem;
border-radius: .25rem;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
cursor: pointer;
position: relative;
-webkit-transition: all .3s ease;
transition: all .3s ease
}

.drinks .drink, .drinks .drink>.weight {


display: -webkit-box;
display: -ms-flexbox;
display: flex
}

.drinks .drink>.description {
width: 100%;
padding: 1rem;
}

.drinks .drink>.description .title {


color: #3d4852;
display: block;
font-weight: 700;
margin-bottom: .25rem;
float: right;
}

.drinks .drink>.description .description {


font-size: .875rem;
font-weight: 500;
color: #8795a1;
line-height: 1.5
}

.drinks .drink>.price {
width: 20%;
color: #09848d;
display: -webkit-box;

110
Vue.js ‫أساسيات إطار العمل‬ Vue CLI ‫إنشاء مشاريع باستخدام‬

display: -ms-flexbox;
display: flex;
padding-top: 1.5rem;
font-family: Crimson Text, serif;
font-weight: 600
}

.drinks .drink>.price .dollar-sign {


font-size: 24px;
font-weight: 700
}

.drinks .drink>.price .number {


font-size: 72px;
line-height: .5
}

.drinks .active-drink, .drinks .drink:hover {


-webkit-box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0
rgba(0, 0, 0, .08);
box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0 rgba(0,
0, 0, .08)
}

.drinks .active-drink:after, .drinks .drink:hover:after {


border-width: 2px;
border-color: #7dacaf;
border-radius: .25rem;
content: "";
position: absolute;
display: block;
top: 0;
bottom: 0;
left: 0;
right: 0
}

.drinks .active-drink, .drinks .drink:hover {


background-color: lightgray;
-webkit-box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0
rgba(0, 0, 0, .08);
box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0 rgba(0,
0, 0, .08)
}

.drinks .active-drink:after, .drinks .drink:hover:after {


border-width: 2px;
border-color: #7dacaf;
border-radius: .25rem;
content: "";
position: absolute;
display: block;

111
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫;‪top: 0‬‬
‫;‪bottom: 0‬‬
‫;‪left: 0‬‬
‫‪right: 0‬‬
‫}‬
‫>‪</style‬‬

‫الحظ معي أنّ الملف السابق يتكوّ ن من األقسام الثالثة التالية‪:‬‬

‫>‪<template‬‬
‫‪...‬‬
‫>‪</template‬‬

‫>‪<script‬‬
‫‪...‬‬
‫>‪</script‬‬

‫>‪<style‬‬
‫‪...‬‬
‫>‪</style‬‬

‫يمكن أن يحت وي أي مل ف يحم ل االمت داد ‪( vue‬وبالت الي أي مك وّ ن) على ثالث ة أقس ام ُنع ّب ر عنها‬

‫بالوس وم <‪ >template‬و <‪ >script‬و <‪ .>style‬الوس م <‪ >template‬إل زامي‪ ،‬والوس مين اآلخ رين‬

‫اختيارين‪ .‬قد تظن أنّ الشيفرة السابقة معقدة بعض الشيء إاّل أ ّنها بس يطة في الواق ع‪ .‬الش يفرة الموج ودة هن ا‬

‫مماثلة لتلك الموجودة في الفصل السابق إلى حدّ كبير‪ .‬حيث عملت على تجمي ع جمي ع الش يفرات البرمجي ة أو‬

‫التنس يقات الخاص ة ب المكوّ ن ‪ drink‬ض من المل ف ‪ .drink.vue‬أي أنن ا اس تطعنا ع زل ك ل م ا يتعلق‬

‫بالمكوّ ن ‪ drink‬ضمن هذا الملف‪ .‬وفي ذلك فائدة كبيرة ّ‬


‫تتمثل في فص ل وتنظيم الش يفرة في أج زاء منطقي ة‬

‫يسهل التعامل معها‪.‬‬

‫يحت وي القس م <‪ >template‬على ش يفرة ‪ HTML‬الخاص ة بق الب المك وّ ن‪ .‬أم ا القس م <‪>script‬‬

‫فيحتوي على ش يفرة ‪ Vue.js‬الالزم ة للمك وّ ن‪ .‬أم ا القس م <‪ >style‬فكم ا ه و واض ح يحت وي على التنس يق‬

‫الالزم للمكوّ ن‪ .‬يمكن في بعض األحيان (كما في حالتنا هن ا) اس تخدام الكلم ة ‪ scoped‬م ع الوس م <‪>style‬‬

‫خاصا بالمكوّ ن الحالي‪.‬‬


‫ً‬ ‫بهدف أن نجعل التنسيق‬

‫أري د الترك يز على الش يفرة الموج ودة ض من القس م <‪ .>script‬الح ظ أ ّن ني ق د اس تخدمت التعليم ة‬

‫‪ .export default‬هذه التعليمة هي تعليم ة ‪ JavaScript‬ووظيفته ا الس ماح بتص دير ك ائن ‪ Vue.js‬ال ذي‬

‫يأتي بعدها إلى خ ارج الوح دة البرمجي ة (المل ف) ‪ drink.vue‬وبالت الي يمكن اس تيراده فيم ا بع د باس تخدام‬

‫أما كائن ‪ Vue.js‬نفسه فهو مماثل لذلك الموجود في الفصل السابق‪.‬‬


‫التعليمة ‪ import‬كما سنرى بعد قليل‪ّ .‬‬

‫‪112‬‬
Vue.js ‫أساسيات إطار العمل‬ Vue CLI ‫إنشاء مشاريع باستخدام‬

ً
‫طفيفا على اس مه ال ذي ك ان في الفص ل‬ ‫ والذي أجريت تعدياًل‬drinksselector ‫جاء اآلن دور المكوّ ن‬

.‫السابق وذلك لجعل األمر أكثر سهولة في التعامل معه‬

ً ‫أنشئ‬
‫ س ّم‬.New File ‫ بالنقر بزر الف أرة األيمن ثم اختي ار األم ر‬Components ‫ملفا جديدً ا ضمن المجلد‬
ّ ‫ سيحوي هذا الملف المك وّ ن األساس ي ال ذي‬.drinksselector.vue ‫الملف الجديد باالسم‬
‫ينظم عملي ة اختي ار‬

.)‫) ضمنه (راجع الفصل السابق‬drink ‫المشروبات (مكوّ نات‬

:‫ الجديد‬drinksselector.vue ‫انسخ الشيفرة البرمجية التالية إلى الملف‬

<template>
<div class="drinks">
<drink
v-for="drink in drinks"
v-bind:key="drink.name"
v-bind:name="drink"
v-on:drink_selected_event="drink_selected_handler"
v-bind:selectedDrink="current_drink"
></drink>
</div>
</template>

<script>
import drink from "./drink.vue";

export default {
name: "drinksselector",
components: {
drink
},
data() {
return {
drinks: ["‫ "بابونج‬,"‫ "زهورات‬,"‫ "شاي أخضر‬,"‫ "قهوة‬,"‫]"شاي‬,
current_drink: null
};
},
methods: {
drink_selected_handler(drink_name) {
this.current_drink = drink_name;
}
}
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

113
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫الحظ معي أنّ القسم <‪ >style‬هنا فارغ‪ .‬قد تتس اءل كي ف س نط ّبق التنس يق الخ اص به ذا المك وّ ن كم ا‬

‫فعلنا في الفصل السابق‪ .‬في الحقيقة سيط ّبق التنسيق الالزم لهذا المكوّ ن من خالل المك وّ ن االبن ‪ .drink‬رغم‬

‫أ ّنن ا وض عنا الكلم ة ‪ scoped‬ض من القس م <‪ >style‬من المك وّ ن ‪ drink‬كم ا م ّر معن ا قب ل قلي ل‪ ،‬إاّل أنّ‬

‫التنسيقات الخاصة بالمكوّ ن ‪ drinksselector‬ستط ّبق علي ه‪ .‬الس بب في ذل ك ه و أنّ المك وّ ن ‪ drink‬ه و‬

‫مكوّ ن متداخل (مكوّ ن ابن) م ع المك وّ ن ‪ drinksselector‬فتطب ق التنس يقات على المك وّ ن األعلى (األب)‬

‫في هذه الحالة بشكل تلقائي‪.‬‬

‫الحظ ً‬
‫أيضا السطر الذي يلي وسم الفتح <‪:>script‬‬

‫;"‪import drink from "./drink.vue‬‬

‫تعم ل ه ذه التعليم ة البرمجي ة على اس تيراد ك ائن الك ائن ‪ drink‬من المل ف ‪ .drink.vue‬اس تطعنا‬

‫مر‬
‫استخدام التعليمة ‪ import‬هنا بسبب وجود التعليمة ‪ export default‬ضمن الملف ‪ drink.vue‬كما ّ‬

‫معنا قبل لحظات‪.‬‬

‫جاء اآلن دور ملف التطبيق الرئيسي ‪ App.vue‬والذي يع د بح د ذات ه مكوّ ًن ا‪ ،‬ولك ّن ه المك وّ ن الرئيس ي في‬

‫تطبيق ما‪ .‬افتح هذا الملف وانسخ إليه الشيفرة التالية‪:‬‬

‫>‪<template‬‬
‫>‪<div‬‬
‫>"‪<div class="header‬‬
‫>‪</span‬مشروبات حسوب>"‪<span id="logo‬‬
‫>‪</div‬‬

‫>"‪<div id="app" class="container‬‬


‫>"‪<div class="content‬‬
‫>‪</h1‬المشروبات المتوفرة>"‪<h1 class="title‬‬
‫>‪<drinksselector></drinksselector‬‬
‫>‪</div‬‬
‫>‪</div‬‬
‫>‪</div‬‬
‫>‪</template‬‬

‫>‪<script‬‬
‫;"‪import drinksselector from "./components/drinksselector.vue‬‬

‫{ ‪export default‬‬
‫‪name: "App",‬‬
‫{ ‪components:‬‬
‫‪drinksselector‬‬
‫}‬
‫;}‬
‫>‪</script‬‬

‫‪114‬‬
Vue.js ‫أساسيات إطار العمل‬ Vue CLI ‫إنشاء مشاريع باستخدام‬

<style>
@import "./assets/styles/app.css";
</style>

‫ الموج ود ب دوره ض من‬components ‫ ض من القس م‬drinksselector ‫الح ظ كي ف أدرجن ا المك وّ ن‬


ً ‫ الح ظ‬.>script< ‫القس م‬
‫ لق د ربطن ا ملف‬.>style< ‫أيض ا كي ف اس تخدمنا التنس يقات ض من القس م‬

.@import ‫ الذي سننشئه بعد قليل بهذا المكوّ ن عن طريق التعليمة‬app.css ‫التنسيقات‬

ّ
‫ ثم اختي ار‬src ‫ وذل ك ب النقر ب زر الف أرة األيمن على المجل د‬src ‫المجل د‬ ّ
‫مجل دً ا جدي دً ا ض من‬ ‫أض ف اآلن‬
ّ
‫ بنفس‬assets ‫المجل د‬ ّ
‫مجل د آخ ر ض من‬ ّ
‫ ثم أنش ئ‬.assets ‫المجلد الجديد باالس م‬ ‫سم‬
ّ .New Folder ‫األمر‬

‫ وانس خ إليه‬app.css ‫ملف ا اس مه‬ ّ


ً styles ‫المجل د‬ ‫ أنش ئ اآلن ض من‬.styles ‫األس لوب الس ابق وس ّمه‬

:‫التنسيقات التالية‬

@import url(//fonts.googleapis.com/earlyaccess/notonaskharabic.css);
body {
height: 100vh;
-webkit-font-smoothing: auto;
-moz-osx-font-smoothing: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: 'Noto Naskh Arabic', serif;
background-color: #ccdcdc;
background-repeat: no-repeat;
background-position: 100% 100%
}

span#logo {
font-weight: 700;
color: #eee;
font-size: larger;
letter-spacing: .05em;
padding-left: .5rem;
padding-right: .5rem;
padding-bottom: 1rem;
float: right;
padding-top: 6px;
margin-right: 20px;
}

.header {
background-color: slategray;
width: 80%;
height: 50px;
margin-left: auto;
margin-right: auto;

115
Vue.js ‫أساسيات إطار العمل‬ Vue CLI ‫إنشاء مشاريع باستخدام‬

h1.title {
text-align: center;
font-size: 1.875rem;
font-weight: 500;
color: #2d3336
}

h2.subtitle {
margin: 8px auto;
font-size: x-large;
text-align: center;
line-height: 1.5;
max-width: 500px;
color: #5c6162
}

.content {
margin-left: auto;
margin-right: auto;
padding-top: 1.5rem;
padding-bottom: 1.5rem;
width: 620px
}

:‫ واحرص على أن يكون محتواه مماثاًل للشيفرة التالية‬index.html ‫انتقل اآلن إلى الملف‬

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-
scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %>
doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

116
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫إنشاء مشاريع باستخدام ‪Vue CLI‬‬

‫لقد انتهينا اآلن من جمي ع الملف ات! اذهب إلى المتص ّفح ثم انتق ل إلى العن وان ‪http://localhost:8080‬‬

‫لتجد تطبيق "مشروبات حسوب" وقد عمل من جديد‪ .‬ولكن هذه المرة باستخدام ‪.Vue CLI‬‬

‫بعد االنتهاء من عملية التطوير بشكل كامل‪ ،‬يمكن استخدام األمر التالي لبناء التطبيق وتجهيزه ضمن بيئ ة‬

‫اإلنتاج (‪ )Production Environment‬لالستخدام النهائي باستخدام األمر‪:‬‬

‫‪npm run build‬‬

‫يمكنك االطالع على المزيد حول هذا الموضوع من خالل من هذا الرابط الذي يحيلك إلى التوثيق الرسمي‬

‫باللغة اإلنجليزية‪.‬‬

‫‪ 7.5‬ختام الفصل‬
‫ّ‬
‫تعلمنا في هذا الفصل كيفية إنشاء تطبيق ‪ Vue.js‬متكامل باس تخدام ق الب ج اهز منحن ا إيّ اه ‪.Vue CLI‬‬
‫ً‬
‫بداية ما هو مفهوم ‪ Vue CLI‬وكيفية إعداده‪ ،‬وكيف ننشئ مشروع جديد باستخدامه‪.‬‬ ‫ّ‬
‫تعلمنا‬

‫كما عملنا على تحويل كامل تطبيق "مشروبات حس وب" من األس لوب الق ديم والمفي د في ح ال ك ّن ا نري د‬

‫تجربة مزايا جديدة‪ ،‬إلى أسلوب ‪ Vue CLI‬بالكامل‪.‬‬

‫بهذا الفصل نكون قد خطونا خطوة مهمة ومتقدمة في العمل مع ‪.Vue.js‬‬

‫‪117‬‬
‫‪ .8‬التعامل مع دخل المستخدم عن‬

‫طريق نماذج اإلدخال‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫بناء هيكل تطبيق بسيط لشرح أفكار الفصل‬ ‫•‬

‫استخدام إطار العمل ‪Bootstrap‬‬ ‫•‬

‫استخدام بنى معطيات متقدمة مع عناصر اإلدخال النصية‬ ‫•‬

‫ِّ‬
‫المعدالت (‪ )Modifiers‬في ‪Vue.js‬‬ ‫•‬

‫التعامل مع مربعات االختيار (‪ )Checkboxes‬وأزرار االنتقاء (‪)Radiobuttons‬‬ ‫•‬

‫التعامل مع القائمة المنسدلة <‪>select‬‬ ‫•‬

‫إرسال البيانات‬ ‫•‬

‫لقد تعاملنا في بداية الكتاب مع عناصر اإلدخال العادية التي يستخدمها المستخدم إلدخال البيان ات ض من‬

‫التوسع في التعام ل م ع ه ذه العناص ر باإلض افة إلى االطالع على‬


‫ّ‬ ‫صفحات الويب‪ .‬سنعمل في هذا الفصل على‬
‫ّ‬
‫سنتعلم كيفية التعامل م ع ُأ ط ر عم ل ‪ CSS‬ج اهزة‪ ،‬حيث‬ ‫طريقة التعامل مع عناصر إدخال ‪ HTML‬جديدة‪ .‬كما‬
‫ً‬
‫مسبقا‪.‬‬ ‫لم يسبق لنا التعامل معها‬
Vue.js ‫أساسيات إطار العمل‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫ بناء هيكل تطبيق بسيط‬8.1

‫ س يتكوّ ن‬.‫ سنبني تطبيق بسيط من أجل ه ذا الفص ل به دف تط بيق األفك ار ال واردة في ه‬،‫كما جرت العادة‬

‫ أنش ئ مش روع جدي د‬.‫ التي ننوي التعام ل معه ا‬HTML ‫تطبيقنا من صفحة واحدة فقط تحوي عدد من عناصر‬
ً
‫مسبقا في الفصل السابق (راج ع الفق رة "بن اء هيك ل تط بيق بس يط لش رح‬ ‫ كما فعلنا‬input-forms-cli ‫وسمه‬
ّ
ّ
)‫المجل د‬( ‫ في فتح المش روع‬Visual Studio Code ‫ استخدم‬،‫ بعد االنتهاء من عملية اإلنشاء‬.)"‫أفكار الفصل‬

.‫توا‬
ًّ ‫الذي أنشأته‬

‫ كم ا فعلن ا في‬HelloWorld.vue ‫ ل ذلك اح ذف المل ف‬App.vue ‫لن نحتاج سوى ملف مكوّ ن واحد وه و‬

:‫ واستبدل المحتوى الحالي بالشيفرة البرمجية التالية‬App.vue ‫ ثم افتح الملف‬.‫الفصل السابق‬

<template>
<div class="container">
<form>
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-
6 col-md-offset-3">
<h1 class="text-right">‫<الملف الشخصي للمستخدم‬/h1>
<hr>
<div class="form-group">
<label class="float-right" for="firstname">
‫<االسم‬/label>
<input
type="text"
id="firstname"
class="form-control"
v-model="userMainData.firstname">
</div>
<div class="form-group">
<label class="float-right" for="lastname">
‫<الكنية‬/label>
<input
type="text"
id="lastname"
class="form-control"
v-model="userMainData.lastname">
</div>
<div class="form-group">
<label class="float-right" for="age">
‫<العمر‬/label>
<input
type="number"
id="age"
class="form-control"
v-model="userMainData.age">

119
Vue.js ‫أساسيات إطار العمل‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

</div>
<div class="form-group">
<label class="float-right" for="password">‫كلمة‬
‫<المرور‬/label>
<input
type="password"
id="password"
class="form-control"
v-model="userMainData.password">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-
6 col-md-offset-3 form-group">
<label class="float-right" for="description">
‫<نبذة‬/label><br>
<textarea
id="description"
rows="5"
class="form-control"
v-model="description"></textarea>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-
6 col-md-offset-3">
<div class="form-group">
<label class="float-right" for="graduate">
<input
type="checkbox"
id="graduate"
value="‫"متخرج‬
v-model="status"> ‫متخرج‬
</label>
<label class="float-right" for="smoker">
<input
type="checkbox"
id="working"
value="‫ًا‬‫"أعمل حالي‬
v-model="status"> ‫ًا‬‫أعمل حالي‬
</label>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-
6 col-md-offset-3 form-group">
<label class="float-right" for="male">
<input
type="radio"
id="male"

120
Vue.js ‫أساسيات إطار العمل‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

value="‫"ذكر‬
v-model="gender"> <span>‫<ذكر‬/span>
</label>
<label class="float-right" for="female">
<input
type="radio"
id="female"
value="‫"أنثى‬
v-model="gender"> <span>‫<أنثى‬/span>
</label>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-
6 col-md-offset-3 from-group">
<label class="float-right" for="subscriptionKind">
‫<نوع االشتراك‬/label>
<select
id="subscriptionKind"
class="form-control"
v-model="selectedSubscription">

<option v-for="kind in subscriptionKinds" v-


bind:key="kind">
{{ kind }}
</option>
</select>
</div>
</div>
<hr>
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-
1">
<button
class="btn btn-primary">‫إرسال‬
</button>
</div>
</div>
</form>
<hr>
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6
col-md-offset-3">
<div class="card card-info">
<div class="card-header text-right">
<h4>‫ُدخلة‬‫<البيانات الم‬/h4>
</div>
<div class="card-body">
<div class="card-text text-right">
<p>‫االسم‬:{{ userMainData.firstname }}</p>
<p>‫الكنية‬:{{ userMainData.lastname }}</p>
<p>‫العمر‬:{{ userMainData.age }}</p>

121
Vue.js ‫أساسيات إطار العمل‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

<p>‫كلمة المرور‬:{{ userMainData.password }}</p>


<p>‫نبذة‬: {{ description }}</p>
<p><strong>‫<الوضع الحالي‬/strong></p>
<ul>
<li v-for="item in status" v-
bind:key="item">{{ item }}</li>
</ul>
<p>‫النوع‬:{{ gender }}</p>
<p>‫نوع االشتراك‬: {{ selectedSubscription }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
export default {
data(){
return {
userMainData:{
firstname:'',
lastname:'',
age:0,
password:'',
},
description:'‫'!اكتب نبذة قصيرة عنك‬,
status:[],
gender:'‫'ذكر‬,
selectedSubscription:'‫'فضي‬,
subscriptionKinds: ['‫ 'عادي‬,'‫ 'فضي‬,'‫]'ذهبي‬
}
}
}
</script>

<style>
@import "./assets/styles/app.css";
</style>

ّ
‫المجل د األخ ير وس ّمه‬ ً ‫ ثم أضف‬styles ‫وسمه‬
‫ملفا جديدً ا ضمن‬ ّ ّ
assets ‫المجلد‬ ّ
‫مجلدً ا جديدً ا ضمن‬ ‫أضف‬

.‫ كما فعلنا في الفصل السابق‬app.css

:‫ على الشكل التالي‬app.css ‫احرص أن تكون محتويات الملف‬

@import url(//fonts.googleapis.com/earlyaccess/notonaskharabic.css);

body{
font-family: 'Noto Naskh Arabic', serif;

122
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫}‬

‫موج ه األوام ر في وين دوز‪ ،‬وبع دها انتق ل إلى مجلد‬


‫ّ‬ ‫احف ظ جمي ع التع ديالت‪ ،‬ثم افتح‬
‫التطبيق ‪ input-forms-cli‬الذي أنشأته قبل قليل‪ّ ،‬‬
‫ونفذ األمر‪:‬‬

‫‪npm run serve‬‬

‫يعمل هذا األمر كما نعلم على تشغيل خادوم الويب الخاص بالتطوير‪ ،‬اذهب اآلن إلى متصفح الويب لديك‪،‬‬

‫وانتقل إلى العنوان ‪ http://localhost:8080‬ليظهر لك شكل شبيه بما يلي‪:‬‬

‫‪123‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫‪ 8.2‬استخدام إطار العمل ‪Bootstrap‬‬

‫ربما تكون قد استغربت قلياًل من عدم استخدامنا ألي مكتبة أو إطار عمل ‪ CSS‬في عمليات التنس يق ال تي‬

‫كنا نجريها في تطبيقاتنا السابقة‪ .‬بداًل عن ذلك‪ ،‬كنا نستخدم شيفرة ‪ CSS‬عادية فحسب في تنسيق التطبيقات‪.‬‬

‫يعود سبب ذلك إلى أ ّنه ال يُ نصح أبدً ا إضافة إطار عمل ‪ CSS‬بالطريقة التقليدية ال تي تتمث ل في اس تخدام‬

‫الوسم <‪ >link‬كما يفعل أغلبنا عند تطوير تطبيقات أمامية (‪ .)Frontend Applications‬يع ود الس بب في‬

‫ذلك ألنّ أغلب ُأ طر عمل ‪ CSS‬تحتوي على شيفرة ‪ JavaScript‬قد ال تكون متوافقة مع ‪.Vue.js‬‬

‫ً‬
‫متوافق ا م ع ‪ Vue.js‬لكي ال تحص ل مفاج آت غ ير‬ ‫علين ا ً‬
‫إذا اس تخدام إط ار عم ل ‪ CSS‬نض من أن يك ون‬

‫مرغوبة! سأتحدث هنا عن استخدام إط ار العم ل الش هير ‪ Bootstrap‬لكي ُنكس ب تطبيقاتن ا تنس يقات جميل ة‬

‫وقوية وبشكل متوافق مع ‪.Vue.js‬‬

‫افتح نافذة موجه األوامر في ويندوز‪ ،‬ثم ّ‬


‫نفذ األمر التالي‪:‬‬

‫‪npm install bootstrap@4.0.0‬‬

‫بعد االنتهاء وعودة موجه األوامر إلى حالته الطبيعية‪ ،‬أضف السطر التالي إلى الملف ‪:main.js‬‬

‫;"‪import "bootstrap/dist/css/bootstrap.min.css‬‬

‫ستصبح محتويات الملف ‪ main.js‬مشابهة لما يلي‪:‬‬

‫'‪import Vue from 'vue‬‬


‫'‪import App from './App.vue‬‬

‫‪Vue.config.productionTip = false‬‬

‫;"‪import "bootstrap/dist/css/bootstrap.min.css‬‬

‫{(‪new Vue‬‬
‫‪render: h => h(App),‬‬
‫)'‪}).$mount('#app‬‬

‫ً‬
‫جاهزا‬ ‫ثم أضفناه إلى التطبيق الخاص بنا ليصبح‬
‫نكون بهذا الشكل قد نصبنا إطار العمل ‪ Bootstrap‬ومن ّ‬
‫مخصصة إلى حد كبير‪.‬‬
‫ّ‬ ‫لالستخدام‪ .‬وهكذا نكون قد استغنينا عن كتابة تنسيقات‬

‫‪124‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫‪ 8.3‬استخدام بنـى معطيات متقدمة مع عنارص اإلدخال النصية‬

‫مغر بعض الشيء‪ ،‬إاّل أ ّننا في الواق ع سنس تخدم ك ائن ‪JavaScript‬‬
‫في الحقيقة رغم أنّ عنوان هذه الفقرة ٍ‬
‫عادي لتمثيل بيانات المستخدم األساس ية ض من تطبيقن ا الح الي‪ .‬إذا راجعت ش يفرة التط بيق األساس ية ال تي‬

‫أوردتها في الفقرة األولى‪ ،‬وتحديدً ا ضمن القسم <‪ >script‬ستجد البنية البسيطة التالية‪:‬‬

‫{‪userMainData:‬‬
‫‪firstname:'',‬‬
‫‪lastname:'',‬‬
‫‪age:0,‬‬
‫‪password:'',‬‬
‫}‬

‫كما أوضحت‪ ،‬سيحمل الحقل ‪ userMainData‬بيانات المستخدم الرئيسية التالي ة على ال ترتيب‪ :‬االس م‪،‬‬

‫الكنية‪ ،‬العمر‪ ،‬كلمة المرور‪.‬‬

‫السؤال هنا‪ ،‬كيف سنربط عناص ر ‪ HTML‬الموافق ة؟ الج واب بس يط‪ ،‬سنس تخدم ‪ v-model‬للرب ط ثن ائي‬

‫االتجاه‪ .‬انظر إلى الشيفرة المقتطعة من الشيفرة الموجودة ضمن القسم <‪:>template‬‬

‫>"‪<div class="form-group‬‬
‫>‪</label‬االسم>"‪<label class="float-right" for="firstname‬‬
‫‪<input‬‬
‫"‪type="text‬‬
‫"‪id="firstname‬‬
‫"‪class="form-control‬‬
‫>"‪v-model="userMainData.firstname‬‬
‫>‪</div‬‬
‫>"‪<div class="form-group‬‬
‫>‪</label‬الكنية>"‪<label class="float-right" for="lastname‬‬
‫‪<input‬‬
‫"‪type="text‬‬
‫"‪id="lastname‬‬
‫"‪class="form-control‬‬
‫>"‪v-model="userMainData.lastname‬‬
‫>‪</div‬‬
‫>"‪<div class="form-group‬‬
‫>‪</label‬العمر>"‪<label class="float-right" for="age‬‬
‫‪<input‬‬
‫"‪type="number‬‬
‫"‪id="age‬‬
‫"‪class="form-control‬‬
‫>"‪v-model="userMainData.age‬‬
‫>‪</div‬‬
‫>"‪<div class="form-group‬‬
‫>‪</label‬كلمة المرور>"‪<label class="float-right" for="password‬‬
‫‪<input‬‬

‫‪125‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫"‪type="password‬‬
‫"‪id="password‬‬
‫"‪class="form-control‬‬
‫>"‪v-model="userMainData.password‬‬
‫>‪</div‬‬

‫الحظ معي كي ف اس تخدمت الحق ل ‪ userMainData‬لرب ط ك ل خاص ية من خصائص ه بعنص ر ‪HTML‬‬

‫الموافق‪ .‬انظر مثاًل ماذا استخدمت لربط حقل االسم ‪:firstname‬‬

‫‪userMainData.firstname‬‬

‫تمام ا! يمكن ك‬
‫لق د اس تخدمت النقط ة للفص ل بين ‪ userMainData‬وبين ‪ firstname‬وه ذا ج ائز ً‬
‫مراجعة باقي عناصر ‪ HTML‬لترى كيف استخدمت نفس الترميز الس ابق‪ .‬الفائ دة في اس تخدام ه ذا األس لوب‬

‫هي في تنظيم وتمثيل البيانات بشكل منطقي في التطبيق‪.‬‬

‫حاول كتابة أي شيء ضمن الحقول األربعة السابقة‪ ،‬ستجد أنّ ذلك سينعكس مباش رة على القس م الس فلي‬

‫خصيص ا لمش اهدة النت ائج ال تي س نجريها على البيان ات في‬


‫ً‬ ‫المدخل ة) من الص فحة‪ ،‬وال ذي جعلت ه‬
‫(البيان ات ُ‬
‫القسم العلوي‪.‬‬

‫ً‬
‫مسبق ا مع عناصر اإلدخال العادية فيما سبق من فصول‪ ،‬وسنتعامل مع أنواع أخ رى‬ ‫في الحقيقة لقد تعاملنا‬

‫من عناصر اإلدخال في ه ذا الفص ل‪ .‬أح د العناص ر الجدي دة ال تي س نتعامل معه ا ه و العنص ر <‪>textarea‬‬

‫الموجه ‪ v-model‬ضمن هذا العنصر‬


‫ّ‬ ‫تماما‪ .‬إذ وضعت‬
‫والذي نتعامل معه كما نتعامل مع عناصر اإلدخال العادية ً‬
‫ليرتبط مع الحقل ‪ description‬كما هو واضح من الشيفرة الواردة في الفقرة األولى من هذا الفصل‪.‬‬

‫‪ 8.4‬المعدالت (‪ )Modifiers‬في ‪Vue.js‬‬

‫يحتاج المبرمج في بعض األحي ان إلى تع ديل س لوك االس تجابة لـ ‪ .Vue.js‬فمثاًل إذا الحظت في تطبيقن ا‬

‫هذا‪ ،‬أنّ أي شيء تكتبه ضمن أي عنصر من عناصر اإلدخال النصية‪ ،‬سينعكس مباشرة ضمن القس م الس فلي‪ .‬ق د‬

‫يكون هذا السلوك غير مرغوب أحيا ًنا‪ ،‬فقد ترغب ربما بأن ال يستجيب ‪ Vue.js‬مباشر ًة لما يكتبه المستخدم‪ ،‬بل‬

‫تؤج ل هذه االستجابة حتى ينتهي المستخدم مما يكتبه وينتقل إلى حقل نصي آخر مثاًل ‪.‬‬
‫بأن ّ‬

‫المع ِّدالت (‪ )modifiers‬حيث يمكن أن نس تخدم‬


‫تمام ا باس تخدام تقني ة ُ‬
‫الس يناريو الس ابق ممكن ً‬
‫يؤجل االستجابة ألي عنص ر إدخ ال مرتب ط مع ه ريثم ا يعم ل المس تخدم‬ ‫ِّ‬
‫المعدل ‪ lazy‬لكي نخبر ‪ Vue.js‬بأن ّ‬
‫على مغ ادرة ه ذا العنص ر (يُ فق ده الترك يز ‪ .)Focus‬انتق ل إلى الش يفرة البرمجي ة الخاص ة بعنص ر اإلدخ ال‬

‫الموجه ‪ v-model‬على الشكل التالي‪:‬‬


‫ّ‬ ‫‪ firstname‬ضمن <‪ >template‬ثم أضف المعدّ ل ‪ lazy‬إلى‬

‫‪126‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫"‪v-model.lazy="userMainData.firstname‬‬

‫احفظ التغييرات‪ ،‬ثم حاول كتابة شيء ما ض من الحق ل ‪ firstname‬وال تح اول مغ ادرة الحق ل‪ ،‬س تجد‬

‫هذه المرة أنّ ذلك لن ينعكس مباش رة ض من القس م الس فلي‪ .‬غ ادر اآلن ذل ك الحق ل إلى حق ل آخ ر‪ ،‬س تجد أنّ‬

‫بيانات االسم قد ظهرت دفعة واحدة في القسم السفلي‪.‬‬

‫فسر دخل المستخدم على أ ّنه رقم ب داًل من النص‪.‬‬


‫توجد معدّ الت أخرى مثل ‪ number‬الذي يُ ستخدم لكي يُ َّ‬
‫الم َ‬
‫دخل‪ .‬يمكن‬ ‫وهناك ً‬
‫أيضا المعدّ ل ‪ trim‬الذي يُ ستخدم للتخلص من المحارف الفارغة على يمين ويسار النص ُ‬
‫استخدام أي معدّ ل كما استخدمنا المع دّ ل ‪ lazy‬قب ل قلي ل‪ ،‬كم ا ويمكن اس تخدامها بش كل م ركّب‪ .‬انظ ر مثاًل‬

‫كيف يمكن استخدام المعدلين ‪ lazy‬و ‪ trim‬بشكل مركّب‪:‬‬

‫"‪v-model.lazy.trim = "userMainData.firstname‬‬

‫‪ 8.5‬التعامل مع مربعات االختيار وأزرار االنتقاء‬


‫ّ‬
‫سنتعلم في هذه الفقرة كيفي ة التعام ل م ع مرب ع االختي ار (‪ )Checkbox‬وزر االنتق اء (‪.)Radiobutton‬‬

‫سنبدأ أواًل مع مربع االختيار‪.‬‬

‫في تطبيقنا هذا‪ ،‬سنستخدم م ربّعي اختي ار للتعب ير عن ك ون المس تخدم متخ ّرج أم غ ير متخ ّرج‪ .‬به دف‬

‫عرفت حقاًل جديدً ا اسمه ‪ status‬على أ ّنه مص فوفة (انظ ر القس م <‪.)>script‬‬
‫التعامل مع هذين المربعين‪ّ ،‬‬
‫الموج ه ‪ v-model‬لك ل من الم ربعين‪ .‬انظ ر الش يفرة‬
‫ّ‬ ‫ربطت هذه المصفوفة مع مربعي االختي ار بإس نادها إلى‬

‫المقتطعة من القسم <‪:>template‬‬

‫>"‪<label class="float-right" for="graduate‬‬


‫‪<input‬‬
‫"‪type="checkbox‬‬
‫"‪id="graduate‬‬
‫"متخرج"=‪value‬‬
‫متخرج >"‪v-model="status‬‬
‫>‪</label‬‬
‫>"‪<label class="float-right" for="smoker‬‬
‫‪<input‬‬
‫"‪type="checkbox‬‬
‫"‪id="working‬‬
‫ًا"=‪value‬‬
‫"أعمل حالي‬
‫ًا >"‪v-model="status‬‬
‫أعمل حالي‬
‫>‪</label‬‬

‫الحظ كيف أ ّننا ربطنا مربعي االختيار بنفس الشكل‪ .‬الذي س يحدث وراء الك واليس‪ ،‬ه و أ ّن ه عن دما يخت ار‬

‫تم اختي اره كعنص ر في‬


‫المس تخدم أح د الم ربّعين س يعمل ‪ Vue.js‬على إدراج قيم ة الحق ل ال ذي ّ‬

‫‪127‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫المصفوفة ‪ ،status‬فإذا اختار المستخدم كال المربعين‪ ،‬فسيدرج ‪ Vue.js‬عنصرين ضمن المصفوفة‪ ،‬يع ّب ر ك ل‬

‫منهما عن قيم ة مر ّب ع االختي ار‪ .‬أي أنّ ع دد عناص ر المص فوفة س يكون مس اويً ا لع دد المربع ات ال تي اختاره ا‬

‫تم ربطها بنفس الشكل‪.‬‬


‫المستخدم‪ ،‬طالما أنّ هذه المربعات قد ّ‬

‫يمكن تط بيق نفس المب دأ تقري ًب ا على أزرار االنتق اء‪ ،‬في تطبيقن ا ه ذا ل دينا زري انتق اء يُ ع ّب ران عن ن وع‬

‫عرفت حقاًل جديدً ا أسميته ‪ gender‬يحمل القيم ة االفتراض ية ذك ر‪ .‬انظ ر معي إلى‬
‫المستخدم (ذكر أم أنثى)‪ّ .‬‬

‫الشيفرة المقتطعة من القسم <‪ >template‬لزري االنتقاء‪:‬‬

‫>"‪<label class="float-right" for="male‬‬


‫‪<input‬‬
‫"‪type="radio‬‬
‫"‪id="male‬‬
‫"ذكر"=‪value‬‬
‫>‪</span‬ذكر>‪v-model="gender"> <span‬‬
‫>‪</label‬‬
‫>"‪<label class="float-right" for="female‬‬
‫‪<input‬‬
‫"‪type="radio‬‬
‫"‪id="female‬‬
‫"أنثى"=‪value‬‬
‫>‪</span‬أنثى>‪v-model="gender"> <span‬‬
‫>‪</label‬‬

‫ً‬
‫بداية أ ّنني قد وضعت القيمة ‪ value‬لك ل من ال زرين الس ابقين لتكون ا "ذك ر" و "أن ثى" على‬ ‫الحظ معي‬

‫الموجه ‪ v-model‬لكل من هذين الزرين‪.‬‬


‫ّ‬ ‫الترتيب‪ .‬الحظ ً‬
‫أيضا كيف استخدمت نفس أسلوب الربط باستخدام‬

‫الذي سيحدث عند بدء تشغيل التطبيق أنّ الحقل ‪ gender‬سيحمل القيمة ذكر بشكل افتراضي كما ذكرت‬

‫قب ل قلي ل‪ ،‬وبالت الي س يختار ‪ Vue.js‬ال زر المع ّب ر عن قيم ة "ذك ر" ألنّ قيمت ه س تكون مماثل ة لقيم ة الحق ل‬

‫يبر ر االختيار االفتراضي لهذا الزر عند البدء بتشغيل التطبيق‪.‬‬


‫‪ gender‬في هذه الحالة‪ .‬وهذا ما ّ‬

‫جرب أن تجري اآلن بعض التجارب على مربعي االختيار وزري االنتقاء‪ ،‬والحظ النتائج التي ستحدث ضمن‬

‫المخصص لعرض البيانات‪.‬‬


‫ّ‬ ‫القسم السفلي‬

‫‪ 8.6‬التعامل مع القائمة المنسدلة‬

‫األول ه و‬ ‫ّ‬
‫نتعلم كيفية الربط مع القائمة المنسدلة <‪ .>select‬سينقسم عملنا هن ا إلى قس مين‪ّ .‬‬ ‫بقي لنا أن‬

‫الموجه ‪.v-model‬‬
‫ّ‬ ‫تعبئة هذه القائمة‪ ،‬والثاني هو الربط مع ‪ Vue.js‬باستخدام‬

‫‪128‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫بالنس بة لتعبئ ة ه ذه القائم ة بالبيان ات األولي ة ال تي س يختار منه ا المس تخدم‪ ،‬فق د ع ّرفت الحق ل‬

‫‪ subscriptionKinds‬وهو على شكل مصفوفة تحوي العناصر التالية‪:‬‬

‫]'ذهبي'‪' ,‬فضي'‪' ,‬عادي'[ ‪subscriptionKinds:‬‬

‫تع ّب ر هذه المصفوفة عن نوع االشتراك الذي يرغب المستخدم باعتماده‪ .‬سيكون لدينا كما هو واض ح ثالث ة‬

‫اشتراكات‪ .‬الشيفرة البرمجية المسؤولة عن تعبئة عنصر القائمة هي التالية‪:‬‬

‫>"‪<option v-for="kind in subscriptionKinds" v-bind:key="kind‬‬


‫}} ‪{{ kind‬‬
‫>‪</option‬‬

‫هذه الشيفرة مأخوذة من القس م <‪ >template‬ب الطبع‪ .‬وهي عب ارة عن حلق ة بس يطة تعم ل على تعبئ ة‬

‫القائمة من خالل توليد عناصر <‪ >option‬العمود الفقري لعنصر القائمة <‪.>select‬‬

‫بالنسبة لعملية الربط فق د ع ّرفت حقاًل آخ ًرا أس ميته ‪ selectedSubscription‬وأس ندت ل ه القيم ة‬

‫اني من عناصر‬ ‫ر الث‬ ‫ة للعنص‬ ‫ة مماثل‬ ‫ذه القيم‬ ‫ظ معي أنّ ه‬ ‫ي"‪ .‬الح‬ ‫ية "فض‬ ‫االفتراض‬

‫المصفوفة ‪ .subscriptionKinds‬بعد ذلك‪ ،‬ربطت عنصر القائمة باستخدام ‪ v-model‬على النحو التالي‪:‬‬

‫"‪v-model="selectedSubscription‬‬

‫وهكذا وعند تشغيل التطبيق للمرة األولى‪ ،‬سيظهر العنصر الثاني فضي وقد اختير بشكل افتراضي‪ .‬جرب‬

‫المخصص لعرض البيانات‪.‬‬


‫ّ‬ ‫اآلن أن تغير خياراتك ضمن القائمة وانظر كيف سينعكس ذلك ضمن القسم السفلي‬

‫‪ 8.7‬إرسال البيانات‬

‫كما هو معلوم‪ ،‬عند وض ع عنص ر ال زر ‪ button‬ض من عنص ر النم وذج ‪ ،form‬فس يؤدي نق ر ه ذا ال زر إلى‬

‫زرا إلرس ال البيان ات كم ا تعلم‪،‬‬


‫إرسال بيانات النموذج (‪ )form‬إلى الخادوم‪ .‬في تطبيقنا البس يط ه ذا‪ ،‬وض عنا ً‬
‫حالي ا أي تطبيق يعمل على الخادوم لمعالجة لبيانات المرسلة‪ ،‬لذلك سنعمل على معالج ة‬
‫ً‬ ‫ولكن بما أ ّنه ليس لدينا‬

‫سنغي ر من سلوك هذا الزر لكي نمنعه من التصرف بالشكل االفتراضي‪.‬‬


‫ّ‬ ‫محليا بشكل وهمي‪ ،‬لذلك‬
‫ًّ‬ ‫البيانات‬

‫الموج ه ‪ .v-on:click‬اعم ل على تع ديل ش يفرة ‪HTML‬‬


‫ّ‬ ‫ِّ‬
‫المعدل ‪ prevent‬م ع‬ ‫سنستخدم لهذه الغاية‬

‫الخاصة بزر اإلرسال لتصبح على النحو التالي‪:‬‬

‫‪<button‬‬
‫"‪v-on:click.prevent="submitInfo‬‬
‫إرسال>"‪class="btn btn-primary‬‬
‫>‪</button‬‬

‫‪129‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫التعامل مع دخل المستخدم عن طريق نماذج اإلدخال‬

‫ا كامل‬ ‫ع هن‬ ‫ابع ‪ .submitInfo‬سأض‬ ‫ف الت‬ ‫م ‪ method‬لكي نتمكّن من تعري‬ ‫يف القس‬ ‫سنض‬

‫قسم <‪ >script‬بعد التعديل‪:‬‬

‫{‪methods:‬‬
‫{)(‪submitInfo‬‬
‫;)"تمت معالجة البيانات"(‪alert‬‬
‫}‬
‫}‬

‫محليا‪ .‬يمكن أن تض ع ب داًل‬


‫ًّ‬ ‫وضعت رسالة بسيطة تشير إلى أنّ البيانات قد تمت معالجتها بش كل افتراض ي‬

‫من هذه الرسالة أن شيفرة قد تجدها مناسبة للبيانات ال تي أدخله ا المس تخدم‪ .‬اله دف هن ا ه و أن تفهم المب دأ‬

‫بحيث يمكنك تكييفه فيما بعد بحسب احتياجاتك‪.‬‬

‫‪ 8.8‬ختام الفصل‬
‫ّ‬
‫تعلمنا في هذا الفصل كيفية استخدام ُأ طر عمل جاهزة لتنسيق المحتوى‪ ،‬حيث استخدمنا في هذا الفص ل‬

‫المخصص ة الس تقبال‬


‫ّ‬ ‫إطار العم ل الش هير ‪ .Bootstrap‬كم ا ّ‬
‫تعلمن ا كي ف نتعام ل م ع ع دد من عناص ر ‪HTML‬‬

‫ال دخل من المس تخدم‪ ،‬حيث تعاملن ا م ع عناص ر اإلدخ ال النص ية البس يطة باإلض افة إلى عنص ر اإلدخ ال ذو‬
‫ً‬
‫وأيض ا مربعات االختيار وأزرار االنتقاء وعنصر القائمة المنسدلة‪.‬‬ ‫األسطر المتعدّ دة <‪>textarea‬‬

‫مخصص ة ب ك وف ق احتياجات ك الخاص ة وذل ك باس تخدام‬


‫ّ‬ ‫في الحقيق ة‪ ،‬يمكن أن تب ني عناص ر إدخ ال‬

‫مر معنا في فصول سابقة‪ .‬أرجو لك الفائدة من هذا الفصل‪ ،‬أراك في الفصول القادمة إن شاء اهلل‪.‬‬
‫المكوّ نات كما ّ‬

‫‪130‬‬
‫‪ .9‬المرشحات ‪ Filter‬والـمخاليط ‪Mixin‬‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫ّ‬
‫المرشح (‪)Filter‬‬ ‫•‬

‫المخلوط (‪)Mixin‬‬ ‫•‬

‫ّ‬
‫المرش حات (‪)Filters‬‬ ‫س نتابع عملن ا في ه ذا الفص ل م ع م يزتين مفي دتين في ‪ Vue.js‬وهم ا‪:‬‬
‫ّ‬
‫سنوض ح‬ ‫والمخاليط (‪ُ .)Mixins‬تعتبر هاتين الميزتين على بساطتهما من المزاي ا المتقدّ م ة نس ً‬
‫بيا في ‪.Vue.js‬‬

‫المقصود بكل منهما في هذا الفص ل‪ ،‬وذل ك بكتاب ة مث الين تطبيق يين بس يطين‪ ،‬لتوض يح الغاي ة من اس تخدام‬

‫جيد‪.‬‬
‫هاتين التقنيتين بشكل ّ‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المرشحات ‪ Filter‬والـمخاليط ‪Mixin‬‬

‫‪ 9.1‬المرشح (‪)Filter‬‬

‫معين على قيم ة حق ل م ا‪،‬‬ ‫ّ‬


‫المرشح (‪ )Filter‬هو م يزة مفي دة في ‪ Vue.js‬يمكن من خالله ا تط بيق إج راء ّ‬
‫بحيث يطرأ تعديل على شكله النهائي عند عرضه للمستخدم‪.‬‬

‫ّ‬
‫المرش حات ال تي ن رغب بكتابته ا ض من قس م خ اص ض من ك ائن ‪ Vue.js‬اس مه ‪.filters‬‬ ‫يمكن تعريف‬

‫وسيطا واحدً ا‪ ،‬ويرجع قيمة بعد التعديل‪.‬‬


‫ً‬ ‫مجرد تابع يقبل‬
‫ّ‬ ‫فالمرشح في الحقيقة عبارة عن‬

‫كمثال على ذل ك‪ ،‬يمكن اس تخدام مرش ح يعم ل على تحوي ل حال ة األح رف إلى كب يرة (بالنس بة لألح رف‬

‫االنجليزية)‪ ،‬رغم أ ّن ها قد تكون ضمن الحقل عبارة عن مزيج بين أحرف صغيرة وكبيرة‪ .‬ربما تكتشف بع د قلي ل‬

‫المرشحات‪ .‬ولكن ت ذكّر‪ ،‬أ ّن ه من األفض ل ً‬


‫دوم ا أن تك ون‬ ‫ّ‬ ‫كبيرا من األمثلة حول اإلمكانات التي قد توفرها‬
‫ً‬ ‫عددا‬
‫ّ‬
‫المرش حات إلج راء عملي ات معق دة على‬ ‫المعالج ة البرمجي ة ض من المرش حات بس يطة وس ريعة‪ .‬فلم تص ّمم‬
‫البيانات ّ‬
‫تكلف الكثير من إمكانيات المعالجة‪ .‬فإذا شعرت مثاًل أنّ األم ر يتج ه إلى مزي د من التعقي د‪ ،‬فأنص حك‬
‫ّ‬
‫المرشحات‪.‬‬ ‫بالتفكير باستخدام الخصائص المحسوبة بداًل من‬

‫برمجيا ّ‬
‫يوضح كيفية كتابة المرشحات‪ .‬أنشئ مشروعً ا جديدً ا باس تخدام األم ر ‪ vue‬من‬ ‫ً‬ ‫لنتناول اآلن مثااًل‬

‫وسمه ‪ vue-filters‬كما يلي‪:‬‬


‫ّ‬ ‫موجه األوامر‬
‫ّ‬

‫‪vue create vue-filters‬‬

‫ثم افتح المشروع باستخدام ‪ .Visual Studio Code‬احذف الملف ‪ HelloWorld.vue‬ثم اس تبدال بم ا‬

‫يلي محتويات الملف ‪:App.vue‬‬

‫>‪<template‬‬
‫>"‪<div id="app‬‬
‫>‪<h1>Filters</h1‬‬
‫}}‪{{text | toUpper‬‬
‫>‪</div‬‬
‫>‪</template‬‬

‫>‪<script‬‬
‫{ ‪export default‬‬
‫{)(‪data‬‬
‫{ ‪return‬‬
‫"‪text:"We will apply a filter on this field.‬‬
‫}‬
‫‪},‬‬
‫{‪filters:‬‬
‫{)‪toUpper(value‬‬
‫;)(‪return value.toUpperCase‬‬

‫‪132‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المرشحات ‪ Filter‬والـمخاليط ‪Mixin‬‬

‫}‬
‫}‬
‫}‬
‫>‪</script‬‬

‫>‪<style‬‬
‫{ ‪#app‬‬
‫;‪font-family: Avenir, Helvetica, Arial, sans-serif‬‬
‫;‪-webkit-font-smoothing: antialiased‬‬
‫;‪-moz-osx-font-smoothing: grayscale‬‬
‫;‪text-align: center‬‬
‫;‪color: #2c3e50‬‬
‫;‪margin-top: 60px‬‬
‫}‬
‫>‪</style‬‬

‫ّ‬
‫المرش ح ‪ toUpper‬ض منه على ش كل ت ابع يقب ل قيم ة‬ ‫انظر إلى القس م الجدي د ‪ filters‬كي ف ع رفت‬
‫ّ‬
‫المرشح حالة األحرف الكبيرة لهذه القيم ة عن طري ق ت ابع ‪JavaScript‬‬ ‫وحيدة ‪ .value‬الحظ كيف يُ رجع هذا‬

‫وهو ‪.toUpperCase‬‬

‫ّ‬
‫المرشح كما هو ظاهر من الشيفرة السابقة‪ .‬نضع المحرف | بعد اس م الحق ل‬ ‫الحظ معي اآلن طريقة تطبيق‬
‫ّ‬
‫المرشح‪ .‬أي كما في التالي‪:‬‬ ‫ّ‬
‫المرشح عليه‪ ،‬ثم نكتب اسم‬ ‫المراد تطبيق‬

‫‪text | toUpper‬‬

‫المرشح لن ّ‬
‫يؤثر على القيمة األصلية للحق ل ‪ text‬إنم ا س يجري‬ ‫ّ‬ ‫وهذا كل شيء‪ .‬ينبغي االنتباه هنا إلى أنّ‬

‫التعديل على الخرج الذي سيظهر للمستخدم فحسب‪ .‬ج رب اآلن معاين ة التط بيق البس يط الس ابق ل ترى كي ف‬

‫أصبح النص يظهر بأحرف طباعية كبيرة‪.‬‬

‫محلي ا (‪ .)Local‬يمكن‬
‫ً‬ ‫ً‬
‫تعريف ا‬ ‫ّ‬
‫المرش حات كقس م ض من ك ائن ‪ Vue.js‬يُ عت بر‬ ‫األسلوب الس ابق في تعري ف‬
‫ّ‬
‫المرش حات بش كل ع ام (‪ )Global‬وذل ك عن طري ق اس تخدام الت ابع ‪ Vue.filter‬ض من‬ ‫تعري ف‬

‫الملف ‪ main.js‬على الشكل التالي‪:‬‬

‫'‪import Vue from 'vue‬‬


‫'‪import App from './App.vue‬‬

‫‪Vue.config.productionTip = false‬‬

‫{)‪Vue.filter('toLower', function(value‬‬
‫;)(‪return value.toLowerCase‬‬
‫)}‬

‫{(‪new Vue‬‬
‫‪render: h => h(App),‬‬

‫‪133‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المرشحات ‪ Filter‬والـمخاليط ‪Mixin‬‬

‫)'‪}).$mount('#app‬‬

‫أما الثاني فه و الت ابع ال ذي‬ ‫ّ‬


‫المرشح العام‪ّ ،‬‬ ‫األول هو اسم‬
‫يٌمرر وسيطان إلى التابع ‪ّ filter‬‬
‫كما هو واضح‪ّ ،‬‬
‫ّ‬
‫المرش ح هن ا ه و ‪ toLower‬وس نجعله مس ؤول عن تحوي ل‬ ‫ّ‬
‫بالمرش ح‪.‬‬ ‫البرمجية الخاصة‬
‫ّ‬ ‫يحتوي على الشيفرة‬

‫األحرف إلى الحالة الطباعية الصغيرة‪.‬‬

‫ّ‬
‫المرش ح المحلي الق ديم‬ ‫ّ‬
‫المرشح العام الجديد ضمن الملف ‪ App.vue‬وذل ك على‬ ‫سنعمل اآلن على تطبيق‬

‫على النحو التالي‪:‬‬

‫}}‪{{text | toUpper | toLower‬‬

‫ّ‬
‫المرشح القديم‪ .‬ال ذي س يحدث اآلن‪ ،‬أنّ محتوي ات‬ ‫ّ‬
‫المرشح الجديد بشكل متسلسل على‬ ‫الحظ كيف طبقنا‬

‫المرشح ‪ toUpper‬أواًل ‪ ،‬مما سيعطينا حالة أحرف طباعية كبيرة‪ ،‬ثم سيتم تط بيق‬
‫ّ‬ ‫الحقل ‪ text‬سيط ّبق عليها‬
‫ّ‬
‫المرشح ‪ toLower‬على النتيجة األخيرة‪ ،‬مما سيؤدي إلى تحويل حالة األحرف إلى الحالة الطباعية الصغيرة‪.‬‬

‫ّ‬
‫المرش ح ‪ toUpper‬ولكن ني فض لت اس تخدامها‬ ‫كان يمكن ب الطبع تط بيق المرش ح ‪ toLower‬فق ط دون‬
‫بالشكل المتسلسل السابق لتوض يح إمكاني ة تط بيق أك ثر من ّ‬
‫مرش ح بنفس ال وقت بش كل متسلس ل كم ا فعلنا‬

‫قبل قليل‪.‬‬

‫‪ 9.2‬المخلوط (‪)Mixin‬‬

‫قد ُتضطر في بعض األحيان إلى كتابة مكوّ نات تشترك بنفس الجزء من الش يفرة البرمجي ة تقري ًب ا‪ .‬رغم أنّ‬

‫أساسا إاّل أ ّنه يشكل عادة برمجية غير جيدة‪ً .‬‬


‫دائما ما نسعى في البرمج ة إلى تجنّب‬ ‫ً‬ ‫ذلك ال يعد مشكلة برمجية‬

‫تكرار الشيفرة البرمجية كما تعلم‪.‬‬

‫جاء المخلوط (‪ )Mixin‬ليوجد حاًل لهذه المسألة‪ ،‬حيث من الممكن جعل ذلك الجزء المشترك من المكوّ ن ات‬

‫ضمنه بحيث تتشارك تلك المكونات بالمخلوط مما يلغي الحاجة إلى تكرار الشيفرة البرمجية‪.‬‬

‫سنحتاج بالتأكيد إلى مثال عملي ّ‬


‫يوض ح ه ذه الفك رة‪ .‬أنش ئ مش روعً ا جدي دً ا وس ّمه ‪ ،vue-mixins‬ثم‬

‫افتح المشروع باستخدام ‪ .Visual Studio Code‬احذف المل ف ‪ ،HelloWorld.vue‬ثم أض ف ملفين ض من‬
‫أيضا ً‬
‫ملفا‬ ‫سمهما على النحو التالي‪ BMI.vue :‬و ‪ .ImperialConverter.vue‬وأضف ً‬ ‫ّ‬
‫المجلد ‪ّ ،components‬‬
‫ّ‬
‫المجلد ‪.src‬‬ ‫اسمه ‪ ConverterMixin.js‬إلى‬

‫فك رة ه ذا التط بيق هي إنش اء مك وّ نين األول ه و ‪ ImperialConverter‬ي وفر إمكاني ة التحوي ل من‬

‫أما المكوّ ن الثاني فه و ‪ BMI‬ووظيفت ه إعط اء تقري ر‬


‫الكيلوغرام والسنتيمتر إلى الباوند والبوصة على الترتيب‪ّ .‬‬

‫‪134‬‬
Vue.js ‫أساسيات إطار العمل‬ Mixin ‫ والـمخاليط‬Filter ‫المرشحات‬

ّ
‫ يش ترك ك ل من المك ونين الس ابقين بج زء من الش يفرة‬.)Body Mass Index( ‫مؤشر كتلة الجسم‬ ‫عن حالة‬

‫ م ع العلم أنّ ك ل من المك ونين الس ابقين مس تقلين‬.ConverterMixin ‫البرمجية موض وع ض من المخل وط‬
ّ
.‫يتعلق بالبيانات‬ ‫تماما فيما‬
ً

‫ على الشيفرة البرمجية المسؤولة عن التحويل من الكيل وغرام إلى‬ConverterMixin ‫يحتوي المخلوط‬

:ConverterMixin.js ‫ انظر محتويات الملف‬.‫ ومن السنتيمتر إلى البوصة‬،‫الباوند‬

export const ConverterMixin = {


data() {
return {
kg_value: 0,
cm_value: 0
};
},
computed: {
to_pounds: function () {
return this.kg_value * 2.20462;
},
to_inches: function () {
return this.cm_value * 0.393701;
}
}
}

‫ و‬kg_value ‫ حيث تحت وي على حقلي بيان ات‬،‫الشيفرة البرمجية الموج ودة في المخل وط بس يطة ج دً ا‬

.to_inches ‫ و‬to_pounds :‫ باإلضافة إلى خاصيتين محسوبتين‬cm_value

ً ‫ فهو بسيط‬ImperialConverter ‫بالنسبة للمكون‬


:‫ انظر للشيفرة البرمجية الموجودة ضمنه‬،‫أيضا‬

<template>
<div>
<h2>Kilograms to Pounds Converter</h2>
<div>
<input type="text" style="width:50px;text-align:center;" v-
model="kg_value" />
<span style="margin-left:8px;">Kg = {{to_pounds}} pounds.</span>
</div>
<br />
<div>
<input type="text" style="width:50px;text-align:center;" v-
model="cm_value" />
<span style="margin-left:8px;">CM = {{to_inches}} Inches.</span>
</div>
</div>
</template>

135
Vue.js ‫أساسيات إطار العمل‬ Mixin ‫ والـمخاليط‬Filter ‫المرشحات‬

<script>
import { ConverterMixin } from "../ConverterMixin";

export default {
name: "ImperialConverter",
mixins: [ConverterMixin],
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

< ‫ مناس بة إلدخ ال البيان ات الم راد تحويله ا وذل ك ض من القس م‬HTML ‫ي وفر ه ذا المك وّ ن عناص ر‬

‫ ثم‬import ‫> فنعمل على استيراد المخلوط باس تخدام التعليم ة‬script< ‫أما بالنسبة للقسم‬
ّ ،>template

‫ الحظ أ ّن ه ال توج د ش يفرة برمجي ة‬.mixins ‫ أ ّننا نريد استخدام هذا المخلوط عن طريق القسم‬Vue.js ‫ُنخبر‬

.‫فعلية ضمن المكون أل ّن ه أغلب المنطق الحسابي يحدث ضمن المخلوط في مثالنا البسيط هذا‬

ّ
‫مؤش ر كتل ة الجس م باإلض افة إلى‬ ‫ فهو المكوّ ن المسؤول (كما أشرنا) على حساب‬BMI ‫أما بالنسبة للمكوّ ن‬
ّ

:‫ انظر إلى الشيفرة البرمجية لهذا المكون‬.‫عرض تقرير بسيط للمستخدم حول القيمة المحسوبة‬

<template>
<div>
<h2>Body Mass Index (BMI)</h2>
<div>
<div>
<span>Weight (Kg):</span>
<input type="text" style="width:50px;text-align:center;" v-
model="kg_value" />
</div>
<br />
<div>
<span>Height (Cm):</span>
<input type="text" style="width:50px;text-align:center;" v-
model="cm_value" />
</div>
<br />
<div>
<span>BMI value: {{bmi_value}}</span> -
<span>{{result}}</span>
</div>
</div>
</div>
</template>

<script>
import { ConverterMixin } from "../ConverterMixin";

136
Vue.js ‫أساسيات إطار العمل‬ Mixin ‫ والـمخاليط‬Filter ‫المرشحات‬

export default {
name: "BMI",
mixins: [ConverterMixin],
computed: {
bmi_value: function () {
return (703 * this.to_pounds) / (this.to_inches *
this.to_inches);
},
result: function () {
if (this.bmi_value < 16) {
return "Severe Thinness";
} else if (this.bmi_value < 17) {
return "Moderate Thinness";
} else if (this.bmi_value < 18.5) {
return "Mild Thinness";
} else if (this.bmi_value < 25) {
return "Normal";
} else if (this.bmi_value < 30) {
return "Overweight";
} else if (this.bmi_value < 35) {
return "Obese Class I";
} else if (this.bmi_value < 40) {
return "Obese Class II";
} else if (this.bmi_value >= 40) {
return "Obese Class III";
} else {
return "Not defined!";
}
},
},
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

‫> فنس تورد عن طريق ه‬script< ‫أم ا القس م‬ ّ ،‫مرة أخرى‬


ّ ،‫> واجه ة المس تخدم‬template< ‫يوفر القسم‬

ImperialConverter ‫تمام ا م ع المك ون‬


ً ‫ ونصرح عن استخدامه كما فعلن ا‬ConverterMixin ‫المخلوط‬
.‫قبل قليل‬

‫ انظ ر إلى الش يفرة البرمجي ة‬.‫ الذي سنس تخدم من خالل ه المك ونين الس ابقين‬App.vue ‫أخيرا الملف‬
ً ‫بقي‬

:‫لهذا الملف‬

<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">

137
Vue.js ‫أساسيات إطار العمل‬ Mixin ‫ والـمخاليط‬Filter ‫المرشحات‬

<imperial-converter/>
<BMI/>
</div>
</template>

<script>
import ImperialConverter from './components/ImperialConverter.vue'
import BMI from './components/BMI.vue'

export default {
name: 'App',
components: {
ImperialConverter,
BMI
}
}
</script>

<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

:‫بعد تشغيل التطبيق ستحصل على واجهة شبيهة بما يلي‬

138
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫المرشحات ‪ Filter‬والـمخاليط ‪Mixin‬‬

‫‪ 9.3‬ختام الفصل‬
‫ّ‬
‫المرش حات (‪)Filters‬‬ ‫تح دثنا في ه ذا الفص ل عن م يزتين جدي دتين من مزاي ا ‪ ،Vue.js‬وهم ا‪:‬‬

‫والمخاليط (‪ ،)Mixins‬حيث قد وجدنا أنّ المرشحات هي عبارة عن شيفرات برمجية بسيطة تعمل على إج راء‬

‫منطق برمجي بسيط وسريع دون الحاجة الستخدام التوابع أو الخاصيات المحسوبة‪.‬‬

‫أما المخاليط فوجدنا أ ّن ها تقنية مفيدة للتخلص من تكرار الشيفرة البرمجية بين المكوّ نات المختلف ة‪ ،‬حيث‬
‫ّ‬

‫يُ وضع القسم البرمجي المشترك بين مكوّ نين أو أكثر ض من مل ف منفص ل دعون اه ب المخلوط‪ ،‬وتعم ل المكوّ ن ات‬

‫المختلفة بعد ذلك على استخدام هذا المخلوط وتتجنّب تكرار الشيفرة البرمجية‪.‬‬

‫‪139‬‬
‫‪ .10‬استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫إنشاء قاعدة بيانات على ‪Google Firebase‬‬ ‫•‬

‫تثبيت مكتبة االتصال باإلنترنت ‪vue-resource‬‬ ‫•‬

‫بناء تطبيق باستخدام ‪ Vue.js‬للقراءة واإلضافة من وإلى قاعدة البيانات‬ ‫•‬

‫إضافة ميزة تعديل البيانات للتطبيق السابق‬ ‫•‬

‫مخصص ة لـ ‪ Vue.js‬له ذا الغ رض‪.‬‬


‫ّ‬ ‫سنتعلم في هذا الفصل كيفية االتصال بخواديم بعيدة باستخدام مكتبة‬

‫وبما أ ّن نا نهتم بتبسيط المعلومة من خالل التركيز على مفهوم جديد مح دّ د‪ ،‬فلن ن دخل في مج ال بن اء تط بيق‬

‫مخصص لكي يتعامل معه تطبيق ‪ ،Vue.js‬ولكن سنعتمد على إنش اء‬
‫ّ‬ ‫خلفية كامل (‪)Backend Application‬‬

‫تطبيق بسيط مجاني على ‪ ،Google Firebase‬وهو عبارة عن خدمة قاعدة بيانات سيتواص ل معه ا تطبيقن ا‬

‫لتوضيح الفكرة المطلوبة‪.‬‬


‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫‪ 10.1‬إنشاء قاعدة بيانات عىل ‪Google Firebase‬‬

‫نتوسع في هذا الفصل في الشرح حول ‪ Google Firebase‬ومزاياها‪ ،‬إذ يمكنك االطالع ح ول المزي د‬
‫ّ‬ ‫لن‬

‫عنه ا من خالل الوث ائق الخاص ة به ا وال تي س أزودك ب البعض منه ا بع د قلي ل‪ .‬يكفي ك اآلن أن تعلم‬

‫منصة تملكها ‪ Google‬وظيفته ا مس اعدة المط وّ رين على بن اء وتحس ين وتنمي ة‬


‫أنّ ‪ Google Firebase‬هي ّ‬

‫التطبيقات التي يعملون عليها‪ ،‬فهي عبارة عن خدم ة خلفي ة (‪ )Backend Service‬يمكن ك من خالله ا إنش اء‬

‫قواع د بيان ات وخ دمات اس تيثاق (‪ )Authentication Services‬وغيره ا من المزاي ا المفي دة لتطبيقات ك‬

‫المختلفة‪ ،‬سواء كانت تطبيقات ألجهزة الجوال أو تطبيقات ويب أو غيرها‪.‬‬

‫دعما‬ ‫بالنس بة إلين ا‪ ،‬سنس تخدم ‪ Firebase‬في ه ذا الفص ل لبن اء قاع دة بيان ات بس يطة كخدم ة ّ‬
‫تمث ل ً‬
‫لتطبيق ‪ Vue.js‬بسيط‪ .‬انتق ل إلى الموق ع الرس مي لـ ‪ ،Firebase‬ثم من الزاوي ة اليم نى العلي ا اخ تر ‪Sign in‬‬

‫لتسجيل الدخول‪ ،‬ستحتاج إلى حساب ‪ Google‬للوصول إلى خدمات ‪.Firebase‬‬

‫بعد تسجيل الدخول بحساب ‪ Google‬ستظهر الصفحة الرئيسية التالية‪:‬‬

‫انقر ‪ Go to console‬من الزاوية اليمنى العليا لالنتقال إلى صفحة الخدمات‪ ،‬ثم انق ر زر ‪Add project‬‬

‫ً‬
‫بداية اسم المشروع باإلضافة إلى الموافقة على‬ ‫(أو ‪ )Create a project‬إلضافة مشروع جديد‪ ،‬سيطلب منك‬

‫االتفاقية كما في الشكل التالي‪:‬‬

‫‪141‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫لق د اخ ترت االس م ‪ vue-remote-servers‬ستض طر ب الطبع إلى اس تخدام اس م آخ ر لمش روعك ألن‬

‫االسم الحالي أصبح محجو ًزا‪ .‬بعد ذل ك انق ر زر ‪ Continue‬لالنتق ال إلى المرحل ة الثاني ة ال تي س يخيرك فيه ا‬

‫بتفعيل ‪ Google Analytics‬من أجل هذا المشروع‪ .‬انقر الزر ‪ Continue‬مجددا لالنتقال إلى المرحلة األخيرة‬

‫قبل إنشاء المشروع‪.‬‬

‫في حال كنت قد اخ ترت تفعي ل ‪ Google Analytics‬في المرحل ة الثاني ة‪ ،‬فس يطلب من ك في المرحل ة‬

‫األخيرة تحديد الحساب الذي ترغب باستخدامه مع ‪( Google Analytics‬سيوفر لك حساب افتراض ي اس مه‬

‫‪ Default Account For Firebase‬أو سيسمح لك بإنشاء حساب جدي د إن أحببت)‪ ،‬بع د تحدي د الحس اب‪،‬‬

‫أم ا إذا لم تفعّ ل ‪ Google Analytics‬من‬


‫س يظهر زر ‪ Create project‬في األس فل‪ ،‬انق ره إلنش اء المش روع‪ّ .‬‬
‫المرحلة الثانية‪ ،‬فسيؤدي ذلك إلى إنشاء المشروع المطلوب فو ًرا‪.‬‬

‫إذا كنت تزور ‪ Firebase‬للمرة األولى‪ ،‬فسيطلب منك في المرحلة األخيرة تحديد المنطقة (الدولة)‬

‫الخاصة بـ ‪ Google Analytics‬باإلضافة إلى الموافقة على بنود االستخدام‪.‬‬

‫بعد إنشاء المشروع سينتقل المتصفح إلى الصفحة الخاصة به‪ .‬من القائمة اليسرى انق ر ال زر ‪ Develop‬ثم‬

‫انقر ‪ Database‬أل ّننا لن نهتم اآلن سوى بقاعدة البيانات‪.‬‬

‫‪142‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫بعد النقر على قاعدة البيانات ستحصل على واجهة تسمح لك ببناء قاعدة بيانات جديدة‪ .‬اس تخدم ش ريط‬

‫التمرير العمودي لكي تنتقل إلى أسفل الصفحة قلياًل حتى تص ل إلى القس م الخ اص بإنش اء قاع دة بيان ات في‬

‫الزمن الحقيقي (‪ )Realtime Database‬كما في الشكل التالي‪:‬‬

‫انقر زر ‪ Create database‬إلنشاء قاعدة البيانات المطلوبة‪ .‬ستظهر ناف ذة ص غيرة تع رض فيم ا إذا كنت‬

‫تريد أن تجعل قاعدة البيانات مفتوحة للجميع بهدف اختبارها‪ ،‬أم مقفلة تحتاج إلى تسجيل دخول‪ .‬سنختار أن‬

‫تكون مفتوحة لجعل األمر أسهل‪ .‬اختر ‪:Start in test mode‬‬

‫‪143‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫الحظ التحذير الذي سيظهر باللون األحمر والذي يفي د ب أنّ أي ش خص سيص بح ق اد ًرا على تع ديل قاع دة‬

‫البيانات أو القراءة منها‪ .‬بعد النقر على زر ‪ Enable‬ستحصل على شكل شبيه بما يلي‪:‬‬

‫أصبحت قاعدة البيانات جاهزة‪ ،‬الحظ من جديد كيف يظهر تحذير باللون األحمر يخبرك أنّ قاعدة البيانات‬
‫مفتوحة ويمكن ألي شخص الوصول إليها‪ .‬الحظ ً‬
‫أيضا الرابط الذي يظهر مباشرة فوق التحذير‪:‬‬

‫‪https://vue-remote-servers.firebaseio.com/‬‬

‫هذا الرابط هو عنوان نقط ة االتص ال لخدم ة قاع دة البيان ات ( ‪ )Database Service Endpoint‬ال تي‬
‫ً‬
‫الحقا في هذا الفصل‪.‬‬ ‫توا‪ .‬سنحتاج إلى هذا الرابط‬
‫أنشأناها ًّ‬

‫‪144‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫قد تتساءل ً‬
‫أيضا أين الجداول ضمن قاعدة البيان ات ه ذه؟ الحقيق ة أنّ قواع د البيان ات ض من ‪Firebase‬‬

‫عبارة عن قواعد بيانات ‪ NoSQL‬لمعرفة المزيد حول قواعد البيانات هذه يمكنك زيارة هذه الصفحة‪.‬‬

‫يمكنك الحصول على المزيد من المعلومات حول ‪ Firebase‬من خالل هذا الرابط الذي يحتوي على‬

‫التوثيق الرسمي له ولكن باللغة اإلنجليزية‪.‬‬

‫تحدث في بعض األحيان تحديثات مستمرة على الواجهات الخاصة بـ ‪ ،Google Firebase‬لذلك فقد‬

‫عما تشاهده في الموقع‪ .‬مع أ ّنني أرجو أاّل يحدث ذلك‪ ،‬منعً ا ألي التباس‬
‫يختلف الشرح الموجود هنا قلياًل ّ‬
‫قد يعتريك عزيزي القارئ‪.‬‬

‫‪ 10.2‬تثبيت مكتبة االتصال باإلنرتنت ‪vue-resource‬‬

‫يمكن بالطبع استخدام أي طريقة لالتص ال ب اإلنترنت من خالل ‪ ،Vue.js‬ولكن ني س أختار في ه ذا الفص ل‬

‫أس لوب مخص ص لـ ‪ .Vue.js‬سنس تخدم مكتب ة اس مها ‪ vue-resource‬يمكن من خالله ا االتص ال ب الخواديم‬

‫ومبسط وبشكل منسجم تقري ًبا مع المكتبات الشهيرة المستخدمة لهذا الغرض‪ .‬يمكنك الوص ول إلى‬
‫ّ‬ ‫بشكل سهل‬

‫المستودع الخاص بهذه المكتبة على ‪ Github‬من خالل هذا الرابط‪ .‬في الحقيقة‪ ،‬أنصحك بزيارة مستودع هذه‬

‫جي د وتتع رف على جمي ع اإلمكاني ات‬


‫المكتبة على ‪ Github‬بعد أن تنتهي من هذا الفصل‪ ،‬لتطل ع عليه ا بش كل ّ‬
‫التي تقدّ مها والتي تسهل عملك كمبرمج‪ .‬في هذا الفصل‪ ،‬سنتحدّ ث عن جزء محدود من هذه المزايا‪.‬‬

‫موجه األوامر ّ‬
‫ونفذ األمر التالي‪:‬‬ ‫ّ‬ ‫لتثبيت ‪ vue-resource‬افتح‬

‫‪npm install vue-resource‬‬

‫بعد االنتهاء من التثبيت يمكن اآلن إضافة هذه المكتبة إلى تطبيق ‪ Vue.js‬وذلك بإضافة السطر الت الي إلى‬

‫قسم المكتبات المستوردة ضمن الملف ‪:main.js‬‬

‫;'‪import VueResource from 'vue-resource‬‬

‫واستخدام التعليمة التالية ضمن الملف ‪ً main.js‬‬


‫أيضا‪:‬‬

‫;)‪Vue.use(VueResource‬‬

‫أم ا كيفية االستخدام الفعلي لهذه المكتبة فسنتعرف عليه في الفقرة التالية‪.‬‬
‫ّ‬

‫‪145‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫ لالتصال باإلنرتنت‬Vue.js ‫استخدام‬

‫ بناء تطبيق للقراءة واإلضافة من وإىل قاعدة البيانات‬10.3


ّ
‫سنتعلم من خالله كيفية االتصال بقاعدة‬ Vue CLI ‫ سنبني تطبيق عملي جديد باستخدام‬،‫كما جرت العادة‬

.Google Firebase ‫البيانات التي أنشأناها في الفقرة األولى من هذا الفصل على‬

:‫موجه األوامر‬
ّ ‫ عن طريق تنفيذ األمر التالي ضمن‬vue-remote-servers ‫أنشئ مشروع جديد اسمه‬

vue create vue-remote-servers

ّ
‫ اح ذف اآلن المل ف‬.Visual Studio Code ‫المجل د الخ اص ب ه عن طري ق‬ ‫ افتح‬،‫بع د إنش اء المش روع‬

:‫ مماثلة لما يلي‬main.js ‫ ثم احرص على أن تكون محتويات الملف‬HelloWorld.vue

import Vue from 'vue'


import VueResource from 'vue-resource';
import App from './App.vue'
import "bootstrap/dist/css/bootstrap.min.css";

Vue.config.productionTip = false

Vue.use(VueResource);

new Vue({
render: h => h(App),
}).$mount('#app')

ً ‫واحرص‬
:‫ مماثلة لما يلي‬App.vue ‫أيضا على أن تكون محتويات الملف‬

<template>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-
offset-3">
<h2>Vue.js Remote Server</h2>
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" v-
model="user.username" />
</div>
<div class="form-group">
<label>First name</label>
<input type="text" class="form-control" v-
model="user.firstname" />
</div>
<div class="form-group">
<label>Last name</label>
<input type="text" class="form-control" v-
model="user.lastname" />

146
Vue.js ‫أساسيات إطار العمل‬ ‫ لالتصال باإلنرتنت‬Vue.js ‫استخدام‬

</div>
<button class="btn btn-primary"
@click="postData()">Add</button>
<br />
<br />
<button class="btn btn-primary"
@click="getData()">Retrieve</button>
<br />
<br />
<ul class="list-group">
<li
class="list-group-item"
v-for="usr in users"
v-bind:key="usr.username"
>{{usr.username}} - {{usr.firstname}} {{usr.lastname}}</li>
</ul>
</div>
</div>
</div>
</template>

<script>
export default {
name: "App",
data() {
return {
user: {
username: "",
firstname: "",
lastname: "",
},
users: [],
};
},
methods: {
postData: function () {
this.$http
.post("https://vue-remote-servers.firebaseio.com/users.json",
this.user)
.then(
(response) => {
console.log(response);

this.user.username = "";
this.user.firstname = "";
this.user.lastname = "";
},
(error) => {
console.log(error);
}
);
},

147
Vue.js ‫أساسيات إطار العمل‬ ‫ لالتصال باإلنرتنت‬Vue.js ‫استخدام‬

getData: function () {
this.$http
.get("https://vue-remote-servers.firebaseio.com/users.json")
.then((response) => {
return response.json();
})
.then((data) => {
const tmpArray = [];

for (let key in data) {


tmpArray.push(data[key]);
}

this.users = tmpArray;
});
},
},
};
</script>

<style>

</style>

:‫ستعطي الشيفرة السابقة الواجهة التالية‬

‫ حيث تتك ون بيان ات‬.‫الفكرة من هذا التطبيق هي في إمكانية اإلضافة المتعددة لمس تخدمين افتراض يين‬

‫ سترس ل‬.)Last name( ‫) والكني ة‬First name( ‫) واالس م‬Username( ‫ اسم المستخدم‬:‫كل مستخدم من‬

148
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫بيانات كل مستخدم مفترض إلى قاعدة البيانات على ‪ .Firebase‬بعد ذلك يمكن استرداد بيانات المس تخدمين‬
‫ً‬
‫مسبقا إلى قاعدة البيانات هذه‪ ،‬من خالل نقر الزر ‪.Retrieve‬‬ ‫المضافة‬
‫ُ‬

‫ً‬
‫مسبقا‪ .‬وهذا ما‬ ‫انتبه إلى أننا سنستخدم في هذا التطبيق تنسيقات ‪ Bootstrap‬على افتراض أ ّنك قد ثبته‬

‫يفسر وجود السطر التالي ضمن ملف ‪:main.js‬‬


‫ّ‬

‫;"‪import "bootstrap/dist/css/bootstrap.min.css‬‬

‫إذا أردت أن تتذكر كيفية استخدام تنسيقات ‪ Bootstrap‬ضمن ‪ Vue.js‬يمكنك مراجعة الفقرة‪" :‬استخدام‬

‫إطار العمل ‪ "Bootstrap‬ضمن الفصل‪" :‬التعامل مع دخل المستخدم عن طريق نماذج اإلدخال"‪.‬‬

‫بالنسبة آللية عمل التطبيق‪ ،‬الحظ معي بداي ًة أن ش يفرة ‪ HTML‬الموج ودة ض من القس م <‪>template‬‬

‫هي بسيطة وواضحة‪ .‬بالنسبة لحقول البيانات المستخدمة‪ ،‬فهي معرفة ضمن القسم <‪ >script‬حيث ع ّرفت‬

‫جمي ع الحق ول ال تي تع ود للمس تخدم المف ترض ض من ك ائن اس مه ‪ user‬موج ود ض من القس م ‪ data‬ض من‬

‫كائن ‪ .Vue.js‬يحت وي ه ذا الك ائن على الحق ول التالي ة‪ username :‬و ‪ firstname‬و ‪ lastname‬كم ا ه و‬
‫ّ‬
‫سنخزن ضمنها بيان ات المس تخدمين ال تي‬ ‫واضح‪ .‬كما يحتوي القسم ‪ً data‬‬
‫أيضا على مصفوفة اسمها ‪users‬‬

‫ّ‬
‫سنوضح ذلك بعد قليل‪.‬‬ ‫سنحصل عليها من قاعدة بيانات ‪ Firebase‬عند نقر زر ‪ Retrieve‬كما‬

‫أما بالنسبة للقسم ‪ methods‬فيحتوي على تابعين فقط‪ postData :‬و ‪ getData‬وهما معالجين لحدثي‬
‫ّ‬

‫النقر على الزرين ‪ Add‬و ‪ Retrieve‬على الترتيب‪.‬‬

‫البرمجية الموجودة ضمنه‪:‬‬


‫ّ‬ ‫بالنسبة للتابع ‪ postData‬تأمل معي الشيفرة‬

‫‪this.$http‬‬
‫‪.post("https://vue-remote-servers.firebaseio.com/users.json",‬‬
‫)‪this.user‬‬
‫(‪.then‬‬
‫{ >= )‪(response‬‬
‫;)‪console.log(response‬‬

‫;"" = ‪this.user.username‬‬
‫;"" = ‪this.user.firstname‬‬
‫;"" = ‪this.user.lastname‬‬
‫‪},‬‬
‫{ >= )‪(error‬‬
‫;)‪console.log(error‬‬
‫}‬
‫;)‬

‫‪149‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫اس تخدمت الك ائن ‪ $http‬من الك ائن ‪ .this‬في الحقيق ة أنّ ه ذا الك ائن ُمت اح فق ط من خالل‬

‫المكتب ة ‪ vue-resource‬ال تي أض فناها في ه ذا الفص ل‪ .‬يحت وي الك ائن ‪ $http‬على ع دة تواب ع مفي دة في‬

‫استقبال وإرسال البيانات عبر اإلنترنت من وإلى خ واديم أو خ دمات (‪ )Services‬كم ا هي خدم ة ‪.Firebase‬‬

‫بما أ ّننا نريد إضافة بيانات جديدة‪ ،‬فسنستخدم التابع ‪ post‬كما هو واضح‪ .‬يقبل هذا التابع وسيطين‪:‬‬

‫الوسيط األول هو عنوان نقطة االتصال للخدمة المراد التواصل معه ا‪ ،‬وهي في حالتن ا ه ذه عن وان خدم ة‬

‫قاعدة البيانات على ‪ Firebase‬التي أنشأناها في هذا الفصل‪:‬‬

‫‪https://vue-remote-servers.firebaseio.com/users.json‬‬

‫الرابط السابق حصلت عليه من الصفحة الخاصة بقاع دة البيان ات ‪ vue-remote-servers‬إذا كنت ت ذكر‬

‫من الفق رة األولى من ه ذا الفص ل‪ ،‬ولكن أض فت علي ه اس م المص در ‪ .users.json‬بم ا أ ّن ني أري د إض افة‬

‫مستخدمين افتراضيين إلى قاعدة البيانات فمن المنطقي إض افة بيان ات ه ؤالء المس تخدمين إلى ج دول (إن‬

‫صح التعب ير) اس مه ‪ .users‬في الحقيق ة في قواع د البيان ات من النم ط ‪ NoSQL‬نس مي مث ل ه ذه الج داول‬
‫ّ‬
‫سنخزن ضمنها بيان ات المس تخدمين‬ ‫بالمجموعات (‪ً .)Collections‬‬
‫إذا فاالسم ‪ users‬هو اسم المجموعة التي‬

‫أما االمتداد ‪ .json‬فهو إلزامي‪.‬‬


‫االفتراضيين‪ّ ،‬‬

‫أما الوسيط الثاني فهو عب ارة عن البيان ات الم راد إرس الها‪ .‬وق د أرس لت الك ائن ‪ user‬جمل ًة واح دة‪ ،‬أل ّن ه‬
‫ّ‬
‫ق ‪v-model‬‬ ‫يحت وي على البيان ات الفرعي ة المطلوب ة وال تي هي مربوط ة ب دورها عن طري‬

‫بعناصر ‪ HTML‬الموافقة‪.‬‬

‫يُ رجع التابع ‪ post‬وعد (‪ )Promise‬لذلك فإ ّننا ُنتبع استدعاء الت ابع ‪ post‬بنقط ة مباش رة وبع دها نكتب‬

‫تمام ا عن د العم ل م ع مكتب ات ‪ JavaScript‬الش هيرة األخ رى‪ .‬يقب ل الت ابع ‪then‬‬
‫التابع ‪ then‬كما ه و م ألوف ً‬
‫برمجي ة ُتع الج ال رد ال ذي حص لت عليه‬
‫ّ‬ ‫األول ه و ت ابع يحت وي على ش يفرة‬ ‫وس يطين ً‬
‫أيض ا‪ :‬الوس يط ّ‬
‫مكتبة ‪ vue-resource‬بعد اتصالها مع الخادوم البعيد‪ .‬انظر إلى الشيفرة التالية‪:‬‬

‫{ >= )‪(response‬‬
‫;)‪console.log(response‬‬

‫;"" = ‪this.user.username‬‬
‫;"" = ‪this.user.firstname‬‬
‫;"" = ‪this.user.lastname‬‬
‫}‬

‫وسيطا اسمه ‪ response‬وهو ّ‬


‫يمثل الرد الذي‬ ‫ً‬ ‫مررنا التابع السابق كتابع سهم (‪ )Arrow Function‬يقبل‬
‫ّ‬
‫األول من ه نالح ظ وج ود‬
‫حص لت علي ه المكتب ة من الخ ادوم‪ .‬ض من ه ذا الت ابع وفي الس طر ّ‬

‫‪150‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫التعليم ة )‪ console.log(response‬لع رض محت وى البيان ات ال واردة من الخ ادوم ض من‬

‫الطرفية (‪ )Console‬الخاصة بأدوات المطوّ ر ض من متص ّفح ال ويب‪ .‬يمكن ك إزال ة ه ذا الس طر إن أحببت‪ّ .‬‬
‫أم ا‬

‫التعليمات الثالث التالية فهدفها تفريغ حقول بيانات المستخدم لإلشارة إلى نجاح العملية‪.‬‬

‫أما الوسيط الثاني للتابع ‪ then‬فهو ً‬


‫أيضا تابع آخر‪ ،‬ولك ّنه يحتوي على الشيفرة البرمجية التي تع الج حال ة‬ ‫ّ‬
‫وجود خطأ ما في عملية اإلرسال‪.‬‬

‫عند هذه النقطة بالتحديد‪ّ ،‬‬


‫أفضل تشغيل التطبيق لنرى ماذا س يحدث عن د إض افة مس تخدم جدي د‪ .‬ش غل‬
‫ّ‬
‫المجلد ‪ vue-remote-servers‬ال ذي يح وي‬ ‫موجه األوامر وضمن‬
‫ّ‬ ‫التطبيق عن طريق تنفيذ األمر التالي ضمن‬

‫ملفات التطبيق‪:‬‬

‫‪npm run serve‬‬

‫ُزر اآلن الص فحة ‪ http://localhost:8080‬لتحص ل على واجه ة التط بيق‪ .‬أدخ ل قيم مناس بة الس م‬

‫توا فهذا دليل على نجاح العملية!‬


‫المستخدم و االسم والكنية‪ ،‬ثم انقر الزر ‪ .Add‬إذا اختفت القيم التي أدخلتها ًّ‬

‫انتقل اآلن إلى الصفحة الخاصة بقاعدة البيانات ‪( vue-remote-servers‬عليك زي ارة الموقع ثم نق ر زر‬

‫‪ Go to console‬وبع د ذل ك اختي ار المش روع ‪ vue-remote-servers‬ثم نق ر زر ‪ Database‬من القائم ة‬

‫وأخيرا اختيار قاعدة البيانات الحقيقية (‪ )Realtime Database‬التي تحمل نفس االسم)‪.‬‬
‫ً‬ ‫اليسرى‪،‬‬

‫بحسب البيانات التي أدخلتها أنا‪ ،‬حصلت على الشكل التالي‪:‬‬

‫الحظ معي اسم قاعدة البيانات ‪ vue-remote-servers‬يظهر في أعلى الشجرة‪ ،‬ثم يظهر اسم المجموعة‬

‫يتفرع عن المجموعة ‪ users‬التي هي بمثابة جدول كما اتفقنا‪ ،‬عقدة تالي ة تحم ل‬
‫ّ‬ ‫‪ users‬في العقدة التي تليها‪.‬‬

‫‪151‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫رم و ًزا مبهم ة‪ ،‬يتف ّرع عنه ا من جدي د البيان ات ال تي أرس لتها إلى قاع دة البيان ات وهي‪ Husam :‬لالسم‬

‫و ‪ Burhan‬للكنية و ‪ husam79‬السم المستخدم‪.‬‬

‫بالنسبة للرموز المبهمة التي تظه ر فهي بمثاب ة مفت اح رئيس ي (‪ )Primary Key‬أو مع ّرف (‪ )ID‬للبيان ات‬

‫التي تقع ضمنها‪ .‬أل ّنه من البديهي إرسال العديد من بيان ات المس تخدمين‪ ،‬فس يعمل ‪ Firebase‬على تخص يص‬

‫شيفرة فريدة تحوي مثل هذه الرموز المبهمة لكل مستخدم مفترض تتم إضافته إلى قاعدة البيانات‪ .‬يمكنك إن‬

‫أحببت إضافة مستخدم آخر لترى كيف يحدث ذلك‪.‬‬

‫البرمجية‬
‫ّ‬ ‫أم ا بالنس بة للت ابع ‪ getData‬فه ا هي الش يفرة‬
‫ك ل الكالم الس ابق ك ان للت ابع ‪ّ .postData‬‬
‫الموجودة ضمنه‪:‬‬

‫‪this.$http‬‬
‫)"‪.get("https://vue-remote-servers.firebaseio.com/users.json‬‬
‫{ >= )‪.then((response‬‬
‫;)(‪return response.json‬‬
‫)}‬
‫{ >= )‪.then((data‬‬
‫;][ = ‪const tmpArray‬‬

‫{ )‪for (let key in data‬‬


‫;)]‪tmpArray.push(data[key‬‬
‫}‬

‫;‪this.users = tmpArray‬‬
‫;)}‬

‫ّ‬
‫المخزن ة ض من قاع دة‬ ‫سنستخدم هذه المرة التابع ‪ get‬من الكائن ‪ $http‬وذل ك للحص ول على البيان ات‬

‫البيانات‪ .‬ال يحتاج هذا التابع كما هو واضح إاّل لوسيط واحد هو نفسه الوسيط ّ‬
‫األول للتابع ‪ post‬الذي تح دثنا‬

‫عنه قبل قليل‪.‬‬

‫أيضا وع د (‪ ،)Promise‬ل ذلك وبنفس النق اش الس ابق فإ ّنن ا ُنتب ع اس تدعاء الت ابع ‪get‬‬
‫يُ رجع التابع ‪ً get‬‬

‫تمام ا‪-‬‬
‫بنقطة مباشرة وبعدها نكتب التابع ‪ .then‬اكتفيت هنا بكتاب ة وس يط واح د للت ابع ‪- then‬وه ذا ج ائز ً‬
‫وهو عبارة عن تابع سهم كما هو واضح يحتوي على تعليمة برمجية واحدة فقط‪:‬‬

‫;)(‪return response.json‬‬

‫وظيفة هذه التعليمة هي إرجاع تمثيل ‪ JSON‬للرد الذي حص لت علي ه المكتب ة بع د اس تدعاء الت ابع ‪.get‬‬

‫وذلك من خالل استدعاء التابع ‪ json‬من الكائن ‪ .response‬وهن ا ينبغي التنوي ه إلى أنّ الت ابع ‪ json‬يُ رج ع‬

‫أيضا وعد (‪ .)Promise‬لذلك فإنّ التعليمة السابقة ُترجع هذا الوعد مما يسمح لنا بوضع نقطة مباش ر ًة بع د‬
‫هو ً‬

‫‪152‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫استدعاء التابع ‪ then‬ثم استدعاء تابع ‪ then‬جديد يقبل وسيط وهو بالطبع عبارة عن تابع س هم جدي د يقب ل‬
‫يطا واح دً ا ً‬
‫أيض ا أس ميته ‪ data‬يحت وي على البيان ات الفعلي ة ال تي حص لنا عليه ا من قاع دة البيان ات‬ ‫وس ً‬

‫على ‪.Firebase‬‬

‫برمجية وظيفته ا اس تخالص‬


‫ّ‬ ‫ما تبقى ضمن تابع السهم الموجود ضمن تابع ‪ then‬األخير عبارة عن شيفرة‬

‫البيانات الخام الواردة من المكتبة ‪ vue-resource‬التي تتواصل مع قاعدة البيانات‪ ،‬وتخزينه ا بش كل مناس ب‬

‫ض من المص فوفة المؤقت ة ‪ .tmpArray‬وبع د انته اء عملي ة االس تخالص‪ُ ،‬تس نَد ه ذه المص فوفة إلى‬

‫الحقل ‪ users‬وهو مصفوفة بالطبع‪ ،‬لتُ عرض البيانات بالشكل المرغوب على المستخدم‪ .‬انظر هذه الشيفرة‪:‬‬

‫;][ = ‪const tmpArray‬‬

‫{ )‪for (let key in data‬‬


‫;)]‪tmpArray.push(data[key‬‬
‫}‬

‫;‪this.users = tmpArray‬‬

‫إنّ سبب وجود حلقة ‪ for‬بالشكل السابق‪ ،‬يع ود إلى ش كل البيان ات ال واردة من المكتب ة ‪vue-resource‬‬

‫وال تي هي عب ارة عن ق اموس (‪ ،)Dictionary‬يك ون المفت اح (‪ )Key‬في ه عب ارة عن المفت اح الرئيس ي (تل ك‬

‫أم ا القيم ة (‪ )Value‬له ذا المفت اح فهي عب ارة عن ك ائن يتض من بيان ات‬
‫الرموز الغريب ة) ألح د المس تخدمين‪ّ ،‬‬
‫المستخدم ( في حالتنا هذه تكون هذه البيانات عبارة عن اسم المستخدم واالسم والكنية كما نعلم)‪.‬‬

‫انظر معي إلى البيانات الخام ال واردة من المكتب ة ‪ ،vue-resource‬وال تي حص لت عليه ا بإض افة تعليم ة‬

‫الكتاب ة إلى الطرفي ة (‪ )Console‬عن د ورود البيان ات من المكتب ة (في الحقيق ة تعم دت إخف اء بعض التواب ع‬

‫األخرى الموجودة ضمن الكائن للتركيز على ما يهمنا هنا)‪:‬‬

‫‪153‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫الش كل الس ابق ه و نتيج ة إض افة مس تخدمين افتراض يين آخ رين إلى قاع دة البيان ات‪ ،‬وبع د نقر‬

‫زر ‪ Retrieve‬بالطبع‪ .‬انظر إلى الشكل التالي لترى كيف ستبدو نفس البيانات ولكن على صفحة الويب‪:‬‬

‫‪154‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫‪ 10.4‬إضافة مزية تعديل البيانات للتطبيق السابق‬

‫من الواضح أنّ تطبيقنا السابق يس مح بإض افة مس تخدمين افتراض يين ج دد‪ ،‬كم ا يس مح بق راءة بيان ات‬
‫هؤالء المستخدمين وعرضهم على الصفحة‪ .‬ولكن من المفيد أن يسمح التطبيق ً‬
‫أيضا بتعديل بيان ات مس تخدم‬
‫ً‬
‫مسبقا‪ .‬يمكن ذل ك ببس اطة من خالل اس تخدام الت ابع ‪ put‬من الك ائن ‪ $http‬حيث يس مح‬ ‫افتراضي موجود‬

‫بمجرد أن نعرف المفتاح الرئيسي (تل ك‬


‫ّ‬ ‫ً‬
‫مسبقا ضمن قاعدة البيانات‬ ‫هذا التابع بتعديل بيانات مستخدم موجود‬

‫الرموز الغريبة) للمستخدم ضمن قاعدة البيانات‪.‬‬

‫كبير ا من الناحية الشكلية على التطبيق السابق لكي يسمح بإنجاز ه ذه الم يزة‪،‬‬
‫ً‬ ‫سأجري في الحقيقة تعدياًل‬

‫ورغم التعديالت الكثيرة التي ستجري على الملف ‪ ،App.vue‬إاّل أ ّنها ُتعتبر في حقيق ة األم ر بس يطة وواض حة‬

‫خصوصا بعد أن وصلنا إلى هذه المرحلة في العمل مع ‪.Vue.js‬‬


‫ً‬

‫إليك الشيفرة البرمجية الجديدة للملف ‪:App.vue‬‬

‫>‪<template‬‬
‫>"‪<div class="container‬‬
‫>"‪<div class="row‬‬
‫‪<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-‬‬
‫>"‪offset-3‬‬
‫>‪<h2>Vue.js Remote Server</h2‬‬
‫>"‪<div class="form-group‬‬
‫>‪<label>Username</label‬‬
‫‪<input type="text" class="form-control" v-‬‬
‫>‪model="user.username" /‬‬
‫>‪</div‬‬
‫>"‪<div class="form-group‬‬
‫>‪<label>First name</label‬‬
‫‪<input type="text" class="form-control" v-‬‬
‫>‪model="user.firstname" /‬‬
‫>‪</div‬‬
‫>"‪<div class="form-group‬‬
‫>‪<label>Last name</label‬‬
‫‪<input type="text" class="form-control" v-‬‬
‫>‪model="user.lastname" /‬‬
‫>‪</div‬‬
‫>‪</div‬‬
‫>‪</div‬‬
‫>"‪<div class="row‬‬
‫‪<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-‬‬
‫>"‪offset-3‬‬
‫"‪<button class="btn btn-primary‬‬
‫>‪@click="postOrPutData()">{{actionButtonTitle}}</button‬‬
‫"‪<button class="btn btn-primary float-right‬‬
‫>‪@click="reset()">Reset</button‬‬

‫‪155‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫ لالتصال باإلنرتنت‬Vue.js ‫استخدام‬

</div>
</div>
<hr>
<div class="row">
<div class="col-2">
<button class="btn btn-primary btn-dark"
@click="getData()">Retrieve</button>
</div>
</div>
<div class="row" v-for="usr in users" v-bind:key="usr.username">
<div class="col-2">{{usr.username}}</div>
<div class="col-4">{{usr.firstname}} {{usr.lastname}}</div>
<div class="col-1">
<button class="btn btn-link"
@click="prepareToSave(usr.id)">Edit</button>
</div>
</div>
</div>
</template>

<script>
export default {
name: "App",
data() {
return {
user: {
username: "",
firstname: "",
lastname: "",
},
users: [],
currentUserIdToSave: "",
actionButtonTitle: "Add",
};
},
methods: {
postOrPutData: function () {
if (this.actionButtonTitle === "Add") {
this.$http
.post(
"https://vue-remote-servers.firebaseio.com/users.json",
this.user
)
.then(
(response) => {
console.log(response);

this.user.username = "";
this.user.firstname = "";
this.user.lastname = "";
},
(error) => {

156
Vue.js ‫أساسيات إطار العمل‬ ‫ لالتصال باإلنرتنت‬Vue.js ‫استخدام‬

console.log(error);
}
);
} else {
this.$http
.put(
"https://vue-remote-servers.firebaseio.com/users/" +
this.currentUserIdToSave +
".json",
this.user
)
.then(
(response) => {
console.log(response);
},
(error) => {
console.log(error);
}
);
}
},
getData: function () {
this.$http
.get("https://vue-remote-servers.firebaseio.com/users.json")
.then((response) => {
return response.json();
})
.then((data) => {
const tmpArray = [];

for (let key in data) {


let withId = data[key];
withId.id = key;
tmpArray.push(data[key]);
}

this.users = tmpArray;
});
},
prepareToSave: function (id) {
for (var i = 0; i < this.users.length; i++) {
if (this.users[i].id === id) {
this.user.username = this.users[i].username;
this.user.firstname = this.users[i].firstname;
this.user.lastname = this.users[i].lastname;
this.currentUserIdToSave = this.users[i].id;

this.actionButtonTitle = "Save";

break;
}
}

157
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫‪},‬‬
‫{ )( ‪reset: function‬‬
‫;"‪this.actionButtonTitle = "Add‬‬
‫;"" = ‪this.currentUserIdToSave‬‬

‫;"" = ‪this.user.username‬‬
‫;"" = ‪this.user.firstname‬‬
‫;"" = ‪this.user.lastname‬‬
‫‪},‬‬
‫‪},‬‬
‫;}‬
‫>‪</script‬‬

‫>‪<style‬‬
‫{ ‪.row‬‬
‫;‪margin-top: 8px‬‬
‫}‬
‫>‪</style‬‬

‫ّ‬
‫سأوضح التعديالت الرئيسية التي طرأت على الش يفرة البرمجي ة‪ .‬في البداي ة أج ريت تع ديالت تنس يقية‬

‫على القسم <‪ >template‬للسماح بإضافة زر جديد اسمه ‪ ،Reset‬باإلضافة إلى أ ّنني غيرت العنص ر المس ؤول‬

‫عن عرض بيانات المستخدمين من قاعدة البيانات (كان عبارة عن قائم ة غ ير مرتب ة ‪ ،)ul‬وذل ك لكي أس تطيع‬

‫إضافة زر تعديل ‪ Edit‬بجوار كل مستخدم للسماح بتعديل بياناته في حال الرغبة بذلك‪.‬‬

‫أضفت ً‬
‫أيضا حقلين جدي دين للقس م ‪ ،data‬األول ه و ‪ currentUserIdToSave‬ووظيفت ه االحتف اظ‬

‫أم ا الحق ل الث اني فه و‬


‫بالمفت اح الرئيس ي للمس تخدم الح الي ال ذي ن رغب بتع ديل بيانات ه‪ّ ،‬‬
‫‪ actionButtonTitle‬والذي جعلت قيمته الحالية تظهر مباشرة على الزر ‪ Add‬الذي سيتغير النص الظ اهر‬

‫أم ا عن دما ن رغب بتع ديل‬


‫عليه بحسب السياق‪ .‬أي عندما نرغب بإضافة مس تخدم جدي د س يظهر النص ‪ّ ،Add‬‬
‫ً‬
‫مسبقا سيظهر النص ‪.Save‬‬ ‫بيانات مستخدم موجود‬

‫بمعنى آخر‪ ،‬سيعمل التطبيق على تنفيذ مجموعة مختلفة من التعليمات البرمجية بحسب النص الذي يظهر‬
‫سي ّ‬
‫نفذ عند النقر على هذا ال زر‪ ،‬فق د غ يرت اس م‬ ‫حالي ا على هذا الزر‪ .‬لذلك فمن المنطقي تغيير اسم التابع الذي ُ‬
‫ً‬
‫التابع من ‪ postData‬إلى ‪ postOrPutData‬لإلشارة إلى وظيفته الجديدة المتمثلة في اإلضافة الجديدة أو‬

‫من‬ ‫ودة ض‬ ‫ة الموج‬ ‫يفرة البرمجي‬ ‫ر إلى الش‬ ‫ً‬


‫بقا‪ .‬انظ‬ ‫ودة مس‬ ‫ات موج‬ ‫ديل على بيان‬ ‫التع‬

‫هذا التابع‪:‬‬

‫{ )"‪if (this.actionButtonTitle === "Add‬‬


‫‪this.$http‬‬
‫(‪.post‬‬
‫‪"https://vue-remote-servers.firebaseio.com/users.json",‬‬

‫‪158‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫‪this.user‬‬
‫)‬
‫(‪.then‬‬
‫{ >= )‪(response‬‬
‫;)‪console.log(response‬‬

‫;"" = ‪this.user.username‬‬
‫;"" = ‪this.user.firstname‬‬
‫;"" = ‪this.user.lastname‬‬
‫‪},‬‬
‫{ >= )‪(error‬‬
‫;)‪console.log(error‬‬
‫}‬
‫;)‬
‫{ ‪} else‬‬
‫‪this.$http‬‬
‫(‪.put‬‬
‫‪"https://vue-remote-servers.firebaseio.com/users/" +‬‬
‫‪this.currentUserIdToSave +‬‬
‫‪".json",‬‬
‫‪this.user‬‬
‫)‬
‫(‪.then‬‬
‫{ >= )‪(response‬‬
‫;)‪console.log(response‬‬
‫‪},‬‬
‫{ >= )‪(error‬‬
‫;)‪console.log(error‬‬
‫}‬
‫;)‬
‫}‬

‫الحظ عبارة ‪ if‬الموجودة في بداية التابع‪ ،‬وانتبه إلى الشرط الموجود ضمنها‪ .‬يختبر هذا الش رط فيم ا إذا‬

‫كانت القيمة الحالية للحقل ‪ actionButtonTitle‬تساوي القيمة ‪ .Add‬فإذا تحقق هذا الش رط‪ ،‬فه ذا يع ني‬
‫أنن ا نري د إض افة مس تخدم جدي د‪ ،‬فتُ ّ‬
‫نف ذ الكتل ة البرمجي ة المتعلق ة بتحق ق ذل ك الش رط‪ ،‬وهي نفس الكتل ة‬

‫أم ا إذا لم يتحق ق الش رط‪ ،‬فه ذا يع ني‬


‫البرمجية التي كانت موجودة ض من الت ابع ‪ postData‬قب ل التع ديل‪ّ .‬‬
‫مسبقا‪ ،‬لذلك ُت ّ‬
‫نفذ الكتل ة البرمجي ة الموافق ة ال تي‬ ‫ً‬ ‫بالتأكيد أ ّن نا في السياق الذي يسمح بتعديل بيانات موجودة‬

‫تستخدم في ه ذه الم رة الت ابع ‪ put‬كم ا ه و واض ح‪ .‬يتم التحكّم في قيم ة الحق ل ‪actionButtonTitle‬‬

‫ض من الت ابعين الجدي دين ‪ prepareToSave‬و ‪ reset‬كم ا س نرى ذل ك بع د قلي ل‪ .‬ل نركّز اآلن قلياًل على‬

‫التابع ‪:put‬‬

‫‪this.$http‬‬
‫(‪.put‬‬
‫‪"https://vue-remote-servers.firebaseio.com/users/" +‬‬
‫‪this.currentUserIdToSave +‬‬

‫‪159‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫‪".json",‬‬
‫‪this.user‬‬
‫)‬

‫األول هو عنوان المصدر ال ذي س نعمل على تع ديل بيانات ه‪ .‬الح ظ معي‬


‫يقبل هذا التابع وسيطين‪ .‬الوسيط ّ‬

‫كيف وضعت اسم الجدول (المجموعة) ‪ users‬يليه قيمة المفتاح الرئيسي الذي حصلت عليه من قيمة الحق ل‬

‫أما الوسيط الثاني فهو بكل بساطة الك ائن‬


‫‪ currentUserIdToSave‬ثم وضعت االمتداد اإللزامي ‪ّ ..json‬‬

‫‪ user‬الذي من المفترض اآلن أن يحمل البيانات المعدّ لة لهذا المستخدم‪ .‬من الممكن أن نجري تعديل على ج زء‬

‫من البيان ات أو على جمي ع البيان ات أو أن ال نج ري أي ة تع ديالت‪ .‬في ك ل حال ة من الح االت الس ابقة س يتم‬

‫استبدال بالقيم الحالية لبيانات الكائن ‪ user‬البيانات القديمة الموجودة ضمن قاع دة البيان ات وال تي له ا نفس‬

‫قيمة المفتاح الرئيسي‪.‬‬

‫كما أوضحت قب ل قلي ل‪ ،‬فق د أض فت ت ابعين جدي دين هم ا‪ prepareToSave :‬و ‪ ،reset‬كم ا أج ريت‬

‫بسيطا للتابع ‪ .getData‬س أبدأ من التع ديل البس يط ال ذي أجريت ه على الت ابع ‪ .getData‬أض فت في‬
‫ً‬ ‫تعدياًل‬

‫الحقيقة حقاًل جديدً ا إلى كل كائن ّ‬


‫يمث ل مستخدم مفترض تم جلبه من قاعدة بيانات‪ .‬انظر معي إلى حلقة ‪for‬‬

‫التي حدث فيها التعديل‪:‬‬

‫{ )‪for (let key in data‬‬


‫;]‪let withId = data[key‬‬
‫;‪withId.id = key‬‬
‫;)]‪tmpArray.push(data[key‬‬
‫}‬

‫أنشأت متغير جديد اسمه ‪ withId‬تنحصر وظيفته في إضافة الحقل ‪ id‬إلى البيانات التي تأتي أصاًل من‬

‫قاعدة البيانات وذلك بهدف االحتفاظ بالمفتاح الرئيسي لكل مستخدم مع بيانات ه األساس ية‪ .‬س نرى س بب ه ذا‬

‫التعديل بعد لحظات‪.‬‬

‫بالنسبة للشيفرة البرمجي ة لك ل من الت ابعين ‪ prepareToSave‬و ‪ reset‬فهي بس يطة للغاي ة‪ .‬بالنس بة‬

‫وسيطا واح دً ا ه و‬
‫ً‬ ‫فيستدعى عند نقر الزر ‪ Edit‬بجوار مستخدم ما‪ .‬يقبل هذا التابع‬
‫للتابع ‪ُ prepareToSave‬‬
‫قيمة المفتاح الرئيسي لذلك المستخدم الذي نرغب بتعديل بياناته‪ .‬بعد ذلك ندخل حلق ة ‪ ،for‬ه دفها الوص ول‬

‫إلى الكائن الذي ّ‬


‫يمث ل المستخدم المراد تعديل بياناته‪ .‬استطعت تحديد هذا الكائن‪ ،‬وذلك بمقارنة قيمة الوس يط‬

‫‪( id‬الذي يحمل قيمة المفتاح الرئيسي للمستخدم المراد تعديله) م ع قيم ة الحق ل ‪ id‬لك ل مس تخدم موج ود‬

‫ض من المص فوفة ‪ .users‬الح ظ هن ا أنّ الحق ل ‪ id‬الموج ود ض من المص فوفة ‪ users‬ق د أض فناه ض من‬

‫التابع ‪.getData‬‬

‫‪160‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫بع د الوص ول إلى الك ائن المطل وب ض من المص فوفة‪ ،‬تعم ل الش يفرة على تح ديث بيان ات الحق ل ‪user‬‬

‫وبالت الي تتم تعبئ ة عناص ر اإلدخ ال على الص فحة ببيان ات المس تخدم الم راد تعديل ه‪ ،‬ثم ُتع دَّ ل قيم ة الحق ل‬

‫‪ currentUserIdToSave‬ليحم ل قيم ة المفت اح الرئيس ي له ذا المس تخدم الم راد تعديل ه (الح ظ أ ّنن ا‬

‫وأيض ا ُتع دَّ ل قيم ة الحق ل‬


‫ً‬ ‫استخدمنا قيمة هذا الحقل ضمن التابع ‪ postOrPutData‬بتمري ره للت ابع ‪،)put‬‬

‫‪ actionButtonTitle‬لتحمل القيمة ‪ ،Save‬وهك ذا نك ون ق د دخلن ا في س ياق حف ظ البيان ات‪ ،‬أي نك ون‬

‫جاهزين لتعديل بيانات المستخدم‪.‬‬

‫يرا بالنس بة للت ابع ‪ reset‬فه و يُ عي د األم ور إلى أص لها األول‪ .‬أي يعم ل على تفري غ حق ول اإلدخ ال‪،‬‬
‫وأخ ً‬
‫ويعم ل على إع ادة الس ياق إلى حال ة إدخ ال مس تخدم جدي د بإس ناد القيم ة ‪ Add‬ض من الحق ل‬

‫حالي ا أي‬
‫ً‬ ‫‪ ،actionButtonTitle‬كما يعمل على تفري غ الحق ل ‪ currentUserIdToSave‬أل ّن ه ال يوج د‬

‫مستخدم نرغب بتعديل بياناته كما هو واضح‪.‬‬

‫إذا شغلت التطبيق بعد هذه التعديالت يُ فترض أن تحصل على شكل شبيه بما يلي (الح ظ أ ّن ني ق د نق رت‬
‫ً‬
‫أيضا الزر ‪:)Retrieve‬‬

‫‪161‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫استخدام ‪ Vue.js‬لالتصال باإلنرتنت‬

‫جرب نقر زر ‪ Edit‬بجوار أحد المستخدمين‪ ،‬ثم عدّ ل بيانات ه‪ ،‬ثم انق ر زر ‪ ،Save‬وبع دها انق ر زر ‪Retrieve‬‬

‫من جديد‪ .‬يجب أن ترى اآلن التعديالت التي أجريته ا على ذل ك المس تخدم‪ .‬انق ر زر ‪ Reset‬للع ودة إلى س ياق‬

‫إضافة مستخدم جديد‪.‬‬

‫عمليا في التطبيقات الحقيقة‪ .‬إذ كان من الواجب‬


‫ً‬ ‫ال يُ عتبر األسلوب الذي اتبعته في تعديل البيانات أسلوبًا‬

‫االنتقال إلى صفحة منفصلة عند النقر على زر ‪ Edit‬لتعديل بيانات المستخدم بشكل مستقل‪ .‬لكنني آثرت‬

‫تماما‬
‫اتباع هذا األسلوب غير العملي اآلن‪ ،‬ألنّ اتباع األسلوب العملي يقتضي الدخول في بحث جديد ً‬
‫ً‬
‫الحقا في فصل منفصل إن شاء اهلل‪.‬‬ ‫وهذا ما سأفعله‬

‫ً‬
‫مسبقا باستخدام التابع ‪ delete‬مع الكائن‬ ‫من الممكن ً‬
‫أيضا إضافة ميزة حذف مستخدم موجود‬

‫األول للتابع ‪.put‬‬


‫‪ $http‬مع تمرير وسيط واحد فقط له‪ .‬هو نفسه الوسيط ّ‬

‫‪ 10.5‬ختام الفصل‬
‫ّ‬
‫وتعلمن ا‬ ‫لقد ّ‬
‫تعل منا الكثير في هذا الفصل كيفية إنشاء قاعدة بيانات بسيطة على ‪ ،Google Firebase‬كما‬
‫كيفية استخدام المكتبة ‪ vue-resource‬إلكساب تطبيقات ‪ Vue.js‬القدرة على االتصال باإلنترنت‪ ،‬وبنينا ً‬
‫أيضا‬
‫مبس ط ّ‬
‫يوض ح كيفي ة التواص ل م ع قاع دة البيان ات على ‪ ،Google Firebase‬وبالت الي كيفي ة‬ ‫تطبيق عملي ّ‬
‫إضافة وقراءة وتعديل البيانات الموجودة ضمنها‪.‬‬

‫مهم ا بالفعل‪ ،‬فمن خالله استطعت للمرة األولى الخروج من "القمقم" والتواص ل م ع الع الم‬
‫يعد هذا الفصل ًّ‬
‫مهم ة ً‬
‫أيض ا ح ول كيفي ة التعام ل م ع ‪ Vue.js‬تل ك المكتبة‬ ‫الخ ارجي‪ .‬س تحمل م ا تبقى من فص ول مف اهيم ّ‬

‫القوية والمرنة‪.‬‬

‫‪162‬‬
‫‪ .11‬بناء تطبيقات ذات صفحة واحدة‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫والتعرف على أجزاءه الرئيسية‪.‬‬


‫ّ‬ ‫بناء هيكل التطبيق‬ ‫•‬

‫تحويل التطبيق الموجود في الفصل السابق إلى تطبيق ‪.SPA‬‬ ‫•‬

‫دة‬ ‫فحة واح‬ ‫د على ص‬ ‫ات تعتم‬ ‫اء تطبيق‬ ‫ة بن‬ ‫ل كيفي‬ ‫ذا الفص‬ ‫ّ‬
‫نتعلم في ه‬ ‫س‬
‫فق ط (‪ )Single Page Applications‬أو اختص ا ًرا ‪ .SPA‬ت ّ‬
‫وفر ‪ Vue.js‬ه ذه اإلمكاني ة من خالل مكتبة‬

‫اسمها ‪ vue-router‬تسمح بتعريف مسارات داخلية ض من التط بيق به دف دعم مفه وم ‪ .SPA‬وتطبيق ات ‪SPA‬‬

‫هي تطبيقات ويب عادية‪ ،‬لك ّنها تختلف عن التطبيقات الكالسيكية في أنّ االنتقال من ص فحة إلى أخ رى ض من‬

‫الموقع ال يحتاج إلى إعادة تحميل كامل الصفحة بما تحويه من ملفات شيفرة نصية وص ور وغيره ا من أص ول‬

‫معرف ة‬
‫الموقع‪ .‬فالذي يحدث في تطبيقات ‪ SPA‬هو أنّ جميع الواجهات المفترض وجوده ا في الموق ع س تكون ّ‬

‫ومحملة إلى حاسوب المستخدم‪ ،‬فيعمل تطبيق ‪ SPA‬فقط على استبدال واجهة مكان واجهة أخرى دون‬
‫ّ‬ ‫ً‬
‫مسبقا‬
‫ً‬
‫كاملة من الخادوم‪.‬‬ ‫الحاجة إلى تحميل الصفحة‬

‫وثيق ا بالفص ل الس ابق أل ّن ه س يعمل على تحوي ل التط بيق المعتم د في الفص ل‬
‫ً‬ ‫ارتباط ا‬
‫ً‬ ‫يرتبط هذا الفصل‬

‫السابق إلى تطبيق يدعم ‪ SPA‬لذلك أنصحك بمراجعة الفصل الس ابق قب ل االس تمرار في ه ذا الفص ل‪ .‬على أي ة‬

‫حال‪ ،‬أرجو أن يكون هذا الفصل ممتعً ا ومفيدً ا‪.‬‬


‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫‪ 11.1‬بناء هيكل التطبيق والتعرف عىل أجزائه الرئيسية‬

‫بنينا في الفصل السابق تطبيق تجريبي بسيط‪ ،‬هدفه التواصل مع قاعدة بيانات موج ودة على ‪،Firebase‬‬

‫عمليا‬
‫ًّ‬ ‫ّ‬
‫ولعلك تذكر‪ ،‬أ ّنني قد أشرت في ذلك الفصل إلى أنّ األسلوب ال ذي اتبعن اه في بن اء ذل ك التط بيق لم يكن‬

‫بسبب أ ّننا قد "حشرنا" جميع وظ ائف التط بيق ض من واجه ة واح دة وه ذا أم ر غ ير عملي ب الطبع‪ .‬ح ان اآلن‬

‫إصالح ذلك العيب وذل ك باس تخدام تقني ة ‪ .SPA‬س نحوّ ل جمي ع وظ ائف التط بيق الس ابق إلى تط بيق جدي د‬

‫سنبنيه في هذا الفصل‪.‬‬

‫موجه األوامر‪:‬‬
‫ّ‬ ‫لنبدأ بإنشاء هيكل التطبيق الجديد بتنفيذ األمر التالي ضمن‬

‫‪vue create vue-spa‬‬

‫لكي ننج ز مفه وم ‪ SPA‬في تطبيقن ا ه ذا‪ ،‬سنس تخدم تقني ة التوجي ه (‪ )Routing‬ال تي عن طريق‬

‫المكتبة ‪ vue-router‬كما أشرنا قبل قليل‪ .‬استخدم األمر التالي لتثبيت المكتبة ‪:vue-router‬‬

‫‪npm install vue-router‬‬

‫ّ‬
‫مجلد التطبيق باس تخدام ‪ Visual Studio Code‬ثم اح ذف المل ف ‪ .HelloWorld.vue‬س نحتاج‬ ‫افتح‬
‫ّ‬
‫والمجل دات من أج ل ه ذا التط بيق‪ .‬انق ر اآلن ب زر الف أرة األيمن على‬ ‫إلى إنش اء مجموع ة جدي دة من الملف ات‬
‫ّ‬
‫المجل د لتخ زين الواجه ات‬ ‫سمه ‪ .views‬سنستخدم هذا‬ ‫ّ‬
‫مجلد جديد ّ‬ ‫ّ‬
‫المجلد ‪ src‬واختر ‪ New Folder‬إلنشاء‬
‫ّ‬
‫المجلد ‪ views‬الذي أنش أناه ت ًّوا الملف ات التالي ة‪ AddUser.vue :‬و‬ ‫المختلفة للتطبيق الخاص بنا‪ .‬أنشئ ضمن‬
‫ّ‬
‫المجل د ‪ .src‬المل ف‬ ‫‪ EditUser.vue‬و ‪ AllUsers.vue‬و ‪ .Home.vue‬أنش ئ ً‬
‫أيض ا المل ف ‪ routes.js‬ض من‬

‫‪ routes.js‬سيحتوي على مسارات العناوين الداخلية التي سنستخدمها ضمن التطبيق‪.‬‬

‫ستحصل بالنتيجة على البنية التالية من الملفات‪:‬‬

‫‪164‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

SPA ‫ تحويل تطبيق إىل تطبيق‬11.2

‫ عن طري ق عب ارة‬vue-router ‫ يجب أواًل أن نس تورد المكتب ة‬،‫لكي نب دأ باس تخدام التوجي ه‬

:‫االستيراد التالية‬

import VueRouter from "vue-router";

ّ
‫وسأوض ح س بب ذل ك بع د‬ routes.js ‫سنضع هذه العبارة (وبشكل مختلف عما اعتدنا عليه) ضمن المل ف‬

:routes.js ‫ انظر اآلن إلى محتويات الملف‬.‫قليل‬

import Vue from "vue";


import VueRouter from "vue-router";
import Home from "./views/Home";
import AddUser from "./views/AddUser";
import EditUser from "./views/EditUser";
import AllUsers from "./views/AllUsers";

Vue.use(VueRouter);

const routes = [
{
path: "/",
name: "Home",
component: Home

165
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫‪},‬‬
‫{‬
‫‪path: "/AddUser",‬‬
‫‪name: "AddUser",‬‬
‫‪component: AddUser‬‬
‫‪},‬‬
‫{‬
‫‪path: "/EditUser",‬‬
‫‪name: "EditUser",‬‬
‫‪component: EditUser‬‬
‫‪},‬‬
‫{‬
‫‪path: "/AllUsers",‬‬
‫‪name: "AllUsers",‬‬
‫‪component: AllUsers‬‬
‫‪},‬‬
‫;]‬

‫{(‪const router = new VueRouter‬‬


‫‪routes‬‬
‫;)}‬

‫;‪export default router‬‬

‫أوضحنا قبل قليل‪ ،‬أنّ وظيفة الملف ‪ routes.js‬هي تعريف مسارات العناوين الداخلي ة ال تي سنس تخدمها‬

‫ضمن التطبيق‪ .‬لننظر اآلن إلى القسم الخاص بتعليمات االستيراد‪:‬‬

‫‪import‬‬ ‫;"‪Vue from "vue‬‬


‫‪import‬‬ ‫;"‪VueRouter from "vue-router‬‬
‫‪import‬‬ ‫;"‪Home from "./views/Home‬‬
‫‪import‬‬ ‫;"‪AddUser from "./views/AddUser‬‬
‫‪import‬‬ ‫;"‪EditUser from "./views/EditUser‬‬
‫‪import‬‬ ‫;"‪AllUsers from "./views/AllUsers‬‬

‫أول تعليمتين واض حتين أم ا بالنس بة للتعليم ات األرب ع األخ رى‪ ،‬فهي الس تيراد الواجه ات ال تي‬
‫أعتقد أنّ ّ‬
‫سنس تخدمها في التط بيق‪ .‬ه ذه الواجه ات هي بطبيع ة الح ال مكوّ ن ات (أل ّنه ا موج ودة ض من ملف ات تحم ل‬

‫االمتداد ‪.)vue‬‬

‫بعد ذلك ُنخبر ‪ Vue.js‬أن يستخدم التوجيه من خالل التعليمة التالية‪:‬‬

‫;)‪Vue.use(VueRouter‬‬

‫وبعد ذلك نصل إلى القسم الخاص بتعريف المسارات‪:‬‬

‫[ = ‪const routes‬‬
‫{‬
‫‪path: "/",‬‬

‫‪166‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫‪name: "Home",‬‬
‫‪component: Home‬‬
‫‪},‬‬
‫{‬
‫‪path: "/AddUser",‬‬
‫‪name: "AddUser",‬‬
‫‪component: AddUser‬‬
‫‪},‬‬
‫{‬
‫‪path: "/EditUser",‬‬
‫‪name: "EditUser",‬‬
‫‪component: EditUser‬‬
‫‪},‬‬
‫{‬
‫‪path: "/AllUsers",‬‬
‫‪name: "AllUsers",‬‬
‫‪component: AllUsers‬‬
‫‪},‬‬
‫;]‬

‫الحظ معي أننا نضع المسارات التي سنستخدمها في التطبيق ضمن مصفوفة اس مها ‪ .routes‬ك ل عنص ر‬

‫من هذه المصفوفة عبارة عن ك ائن ل ه الحق ول‪ path :‬و ‪ name‬و ‪ .component‬انظ ر مثاًل إلى الك ائن ّ‬
‫األول‬

‫من هذه المصفوفة‪:‬‬

‫{‬
‫‪path: "/",‬‬
‫‪name: "Home",‬‬
‫‪component: Home‬‬
‫}‬

‫ّ‬
‫ويمث ل‬ ‫عرف المسار الخاص بها ليكون ‪/‬‬ ‫ّ‬
‫يمث ل هذا الكائن المسار الخاص بالصفحة الرئيسية للتطبيق‪ .‬فهو يُ ّ‬
‫الصفحة الرئيسية في تطبيقات الويب عادة‪ .‬أي يمكن الوصول إلى هذه الواجهة من خالل الرابط التالي‪:‬‬

‫‪http://www.yourdomain.com/#/‬‬

‫ً‬
‫الحقا في ه ذا‬ ‫أما اسم هذا المسار فهو ‪ ( Home‬من الممكن أن نصل إلى أي مسار من خالل اسمه كما سنرى‬
‫ّ‬
‫الفص ل)‪ .‬بالنس بة للواجه ة ال تي س ترتبط به ذا المس ار فهي ‪( Home‬انظ ر إلى تعليم ة االس تيراد الخاصة‬

‫بالواجهة ‪.)Home.vue‬‬

‫تماما‪ ،‬فإذا أخذنا مثاًل المسار الثاني فنج د أنّ المس ار ال ذي‬
‫بالنسبة لباقي المسارات فتعمل بنفس األسلوب ً‬
‫يُ شير إليه هو ‪ /AddUser‬أي أ ّن نا نستطيع الوصول إليه عن طريق رابط مماثل لما يلي‪:‬‬

‫‪http://www.yourdomain.com/#/AddUser‬‬

‫‪167‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫متغي ر جدي د اس مه ‪ router‬ونس ند‬


‫ّ‬ ‫سنعرف‬
‫ّ‬ ‫نأتي إلى القسم التالي في الشيفرة البرمجية السابقة‪ ،‬حيث‬

‫عمليات التوجيه ضمن التطبيق‪:‬‬


‫ّ‬ ‫الموجه الذي سيدير‬
‫ّ‬ ‫إليه كائن جديد ّ‬
‫يمثل‬

‫{(‪const router = new VueRouter‬‬


‫‪routes‬‬
‫;)}‬

‫توا إلى ‪( VueRouter‬وهو مع ّرف ض من إح دى‬


‫مررنا المصفوفة التي عرفنا ضمنها المسارات ًّ‬
‫الحظ كيف ّ‬
‫الموجه ‪ router‬خارج هذا الملف‪.‬‬
‫ّ‬ ‫وأخيرا نضع التعليمة المسؤولة عن تصدير‬
‫ً‬ ‫تعليمات االستيراد)‪.‬‬

‫لننتقل اآلن إلى الملف ‪:main.js‬‬

‫‪import‬‬ ‫'‪Vue from 'vue‬‬


‫‪import‬‬ ‫'‪App from './App.vue‬‬
‫‪import‬‬ ‫'‪router from './routes.js‬‬
‫‪import‬‬ ‫;"‪"bootstrap/dist/css/bootstrap.min.css‬‬
‫‪import‬‬ ‫;'‪VueResource from 'vue-resource‬‬
‫‪import‬‬ ‫;"‪VueSimpleAlert from "vue-simple-alert‬‬

‫‪Vue.config.productionTip = false‬‬

‫;)‪Vue.use(VueResource‬‬
‫;)‪Vue.use(VueSimpleAlert‬‬

‫{(‪new Vue‬‬
‫‪router,‬‬
‫‪render: h => h(App),‬‬
‫)'‪}).$mount('#app‬‬

‫أول تعليم تي اس تيراد‬


‫نستورد ضمن هذا المل ف المكتب ات ال تي س نحتاج إليه ا ض من التط بيق‪ .‬أعتق د أنّ ّ‬
‫الموج ه ‪ router‬ال تي بنين اه مس ً‬
‫بقا ض من المل ف‬ ‫ّ‬ ‫واض حتين ً‬
‫أيض ا‪ .‬بالنس بة للتعليم ة الثالث ة‪ ،‬فهن ا نس تورد‬

‫أم ا بالنسبة للتعليمات الثالث الباقية فهي على الترتيب‪:‬‬


‫‪ّ ،routes.js‬‬

‫استيراد مكتبة ‪ Bootstrap‬للتنسيق‪.‬‬ ‫•‬

‫استيراد المكتبة ‪ vue-resource‬لالتصال بالخواديم البعيدة‪ ،‬والتي تعاملنا معها في الفصل السابق‪.‬‬ ‫•‬

‫ً‬
‫مسبقا‪ .‬والهدف منه ا ع رض رس ائل‬ ‫استيراد مكتبة جديدة اسمها ‪ vue-simple-alert‬لم نتعامل معها‬ ‫•‬
‫ّ‬
‫سنتعلم بعد قليل كيفية التعامل معها‪.‬‬ ‫ذات تنسيق جميل للمستخدم‪،‬‬

‫موجه األوامر‪:‬‬
‫ّ‬ ‫ستحتاج بالتأكيد إلى تثبيت المكتبة ‪ vue-simple-alert‬بتنفيذ األمر التالي ضمن‬

‫‪npm install vue-simple-alert‬‬

‫‪168‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

:‫الحظ اآلن التعليمتين التاليتين‬

Vue.use(VueResource);
Vue.use(VueSimpleAlert);

‫ ن أتي اآلن‬.VueSimpleAlert ‫ و‬VueResource :‫ أن يس تخدم الك ائنين‬Vue.js ‫هاتين التعليمتين إلخبار‬

:‫إلى آخر تعليمة ضمن هذا الملف‬

new Vue({
router,
render: h => h(App),
}).$mount('#app')

‫ ولكن الح ظ معي هن ا كي ف أض فت ك ائن‬،‫ وق د اس تخدمنا مثله ا م را ًرا‬،‫التعليم ة الس ابقة مألوف ة‬

‫الموجه‬
ّ ‫ ولن يعم ل التوجي ه في التط بيق م ا لم نض ف ك ائن‬،‫ ه ذه اإلض افة ض رورية‬.‫ إليه ا‬router ‫لموج ه‬
ّ
.‫بهذه الصورة‬

.‫ باإلضافة إلى ملفات واجهات التطبيق‬App.vue ‫سنستعرض فيما يلي الملف‬

App.vue ‫ الملف‬11.2.1

:‫ستكون محتويات هذا الملف على الشكل التالي‬

<template>
<div id="app">
<nav id="nav">
<p class="logo-place">Vue.js Remote Server (Enhanced)</p>
<ul class="nav-links">
<li class="links">
<router-link to="/">Home</router-link>
</li>
<li class="links">
<router-link to="/AddUser">Add User</router-link>
</li>
<li class="links">
<router-link to="/AllUsers">All User</router-link>
</li>
</ul>
</nav>
<router-view></router-view>
</div>
</template>

<script>
export default {
name: "App",

169
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

components: {},
};
</script>

<style>

#nav {
display: flex;
margin-bottom: 24px;
}

#nav a {
font-weight: bold;
color: #2c3e50;
text-decoration: none;
margin-right:12px;
}

#nav a.router-link-active {
color: #ab26ab;
}

.nav-links {
padding-right: 20px;
list-style: none;
display: flex;
margin:21px 0 0 0;
}

.links-hover {
text-decoration: underline;
}

.logo-place {
font-size: 20px;
color: purple;
font-weight: bold;
margin:16px 0 0 16px;
}
</style>

‫ الح ظ أواًل وج ود وس م جديد‬.‫هن اك أم ران جدي دان س نتحدث عنهم ا بالنس بة له ذا المل ف‬

،vue-router ‫ في الحقيق ة أنّ ه ذا الوس م م ا ه و إاّل مك وّ ن موج ود ض من المكتب ة‬.router-view ‫اس مه‬

‫ كما أش رنا‬SPA ‫ ّألن تطبيقات‬.‫ووظيفته تحديد مكان إدراج واجهات التطبيق المختلفة ضمن الصفحة الرئيسية‬

.‫ ال تتطلب إعادة تحميل الصفحة بالكامل عندما يريد المستخدم االنتقال إلى واجهة مختلفة‬،‫من قبل‬

170
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

ً ‫ وهو مكوّ ن‬router-link ‫األمر اآلخر هو وجود وسم جديد آخر وهو‬
‫ وظيف ة‬.‫أيضا ضمن نفس المكتب ة‬

‫ فمثاًل الح ظ‬.‫المعرف ة ض منه‬


ّ ‫> بحيث ينتق ل إلى إح دى الواجه ات‬a< ‫هذا المكوّ ن توليد عنصر ارتباط تش عبي‬

:‫ التالي التي أخذته من الشيفرة السابقة‬router-link ‫معي المكوّ ن‬

<router-link to="/AddUser">Add User</router-link>

‫وجه إلي ه‬
َّ ‫) ال ذي س ُي‬Path( ‫ و هي مس ؤولة عن تحدي د المس ار‬to ‫نجد من المقطع الس ابق وج ود الس مة‬
:‫وجه المستخدم إلى المسار الكامل التالي‬
َّ ‫سي‬
ُ ‫ في المثال السابق‬.‫المستخدم عند نقر هذا الرابط‬

http://www.yourdomain.com/#/AddUser

.to ‫ كقيمة للسمة‬/AddUser ‫وذلك بسبب وجود‬

Home.vue ‫ الملف‬11.2.2

:‫وهو ملف الصفحة الرئيسية في التطبيق وستكون محتوياته على الشكل التالي‬

<template>
<div class="container">
<div class="row">
<div class="col-6 offset-2">
<h1>Welcome in our website!</h1>
</div>
</div>

<div class="row" style="margin-top:30px;">


<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">ِ
Add User</h5>
<p class="card-text">Add a new user to the Firebase
experimental website.</p>
<router-link class="btn btn-primary" to="/AddUser">Add a
user</router-link>
</div>
</div>

<div class="card" style="width: 18rem; margin-left:18px;">


<div class="card-body">
<h5 class="card-title">All User</h5>
<p class="card-text">Display all users info from the
Firebase experimental website.</p>
<router-link class="btn btn-primary" to="/AllUsers">Get all
users</router-link>
</div>
</div>
</div>
</div>

171
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

</template>

<script>
export default {
name: "Home",
};
</script>

‫ س وى أ ّن ك تمتل ك الح ق في إض افة أص ناف تنس يقية إلى‬،‫ال يوج د في الش يفرة الس ابقة أي جدي د‬

:‫ كما في‬router-link ‫المكوّ ن‬

<router-link class="btn btn-primary" to="/AddUser">Add a user</router-


link>

:‫ستبدو هذه الواجهة على النحو التالي‬

AddUser.vue ‫ الملف‬11.2.3

‫ انظ ر معي إلى الش يفرة البرمجي ة‬.‫يحتوي هذا الملف على الواجهة المسؤولة عن إض افة مس تخدم جدي د‬

:‫الخاصة به‬
ّ

<template>
<div class="container">
<div class="row">
<div class="col-6 offset-2">
<h1>Add User</h1>
</div>
</div>
<div class="row">
<div class="col-6 offset-2">
<div class="container">

172
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

<div class="row">
<div class="col-12">
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" v-
model="user.username" />
</div>
<div class="form-group">
<label>First name</label>
<input type="text" class="form-control" v-
model="user.firstname" />
</div>
<div class="form-group">
<label>Last name</label>
<input type="text" class="form-control" v-
model="user.lastname" />
</div>
</div>
</div>
</div>
<button class="btn btn-primary float-left" style="margin-
left:12px;" @click="postUser()">Add</button>
</div>
</div>
</div>
</template>

<script>
export default {
name: "AddUser",
methods: {
postUser() {
this.$http
.post("https://vue-remote-servers.firebaseio.com/users.json",
this.user)
.then(
() => {
this.user.username = "";
this.user.firstname = "";
this.user.lastname = "";

this.$alert("A new user is added.", "Operation Succeeded",


"success");
},
(error) => {
console.log(error);
}
);
},
},
data() {

173
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫{ ‪return‬‬
‫{ ‪user:‬‬
‫‪username: "",‬‬
‫‪firstname: "",‬‬
‫‪lastname: "",‬‬
‫‪},‬‬
‫;}‬
‫‪},‬‬
‫;}‬
‫>‪</script‬‬

‫>‪<style scoped‬‬
‫{ ‪.row‬‬
‫;‪margin-top: 8px‬‬
‫}‬
‫>‪</style‬‬

‫الشيفرة البرمجية الموجودة ضمن هذا الملف تماثل تقري ًبا تلك ال تي ك انت مس ؤولة عن إض افة مس تخدم‬

‫تنظيم ا‪ ،‬بس بب أ ّنن ا نع زل هن ا عملي ة إض افة‬


‫ً‬ ‫جديد في الفصل الماضي‪ .‬مع مالحظة أنّ الشيفرة أصبحت أك ثر‬

‫مستخدم جديد عن غيرها من العمليات‪.‬‬

‫خدمت ضمن هذه الواجهة‪ ،‬وهي رسالة ستُ َ‬


‫عرض للمستخدم في ح ال‬ ‫على أية حال توجد ميزة جديدة استُ ِ‬
‫نجاح عملية اإلضافة إلى قاعدة بيانات ‪:Firebase‬‬

‫;)"‪this.$alert("A new user is added.", "Operation Succeeded", "success‬‬

‫كما كان من الممكن إضافة رسالة شبيهة بالرسالة السابقة في ح ال فش لت عملي ة اإلض افة للمس تخدم (لم‬

‫ُأ ضف مث ل ه ذه الرس الة إلى الش يفرة الس ابقة)‪ .‬الت ابع ‪ $alert‬موج ود ض من المكتب ة ‪vue-simple-alert‬‬

‫منس قة للمس تخدم‪ .‬بالنس بة لمثالن ا ه ذا‪ ،‬س نعرض رس الة ُتخ بر‬
‫وظيفته كما هو واض ح‪ ،‬ه و في ع رض رس الة ّ‬
‫المستخدم بأنّ عملية إضافة بيانات المس تخدم إلى قاع دة بيان ات ‪ Firebase‬ق د تمت بنج اح‪ّ .‬‬
‫يمث ل الوس يط‬

‫أم ا الوس يط‬


‫األول للتابع ‪ $alert‬الرسالة التي نريد عرضها‪ ،‬أما الوسيط الثاني فهو عنوان صندوق الرس الة‪ّ ،‬‬
‫ّ‬
‫الث الث فه و ن وع الرس الة وهي في حالتن ا ه ذه ‪ .success‬توج د أن واع أخ رى للرس ائل ال تي يمكن‬
‫ّ‬
‫المتعلق ة به ذه المكتب ة‬ ‫للت ابع ‪ $alert‬عرض ها مث ل ‪ success‬و ‪ .error‬لالطالع على المزي د من الوث ائق‬

‫يمكنك زيارة الصفحة الرسمية لها‪.‬‬

‫ستبدو هذه الواجهة على النحو التالي‪:‬‬

‫‪174‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

AllUsers.vue ‫ الملف‬11.2.4

‫يحتوي هذا المل ف على الواجه ة الخاص ة بع رض بيان ات المس تخدمين ال ذين س بق إض افتهم إلى قاع دة‬

:‫البرمجية الخاصة به‬


ّ ‫ انظر إلى الشيفرة‬.Firebase ‫بيانات‬

<template>
<div class="container">
<div class="row">
<h2>All Users</h2>
<button style="margin-left:16px;" class="btn btn-primary"
@click="getData()">Retrieve</button>
</div>
<hr />
<div class="row" v-for="usr in users" v-bind:key="usr.username">
<div class="col-2">{{usr.username}}</div>
<div class="col-4">{{usr.firstname}} {{usr.lastname}}</div>
<div class="col-1">
<router-link :to="{name: 'EditUser', params:
{user:usr}}">Edit</router-link>
</div>
</div>
</div>
</template>

<script>
export default {
data() {
return {
user: {
username: "",
firstname: "",
lastname: "",
},

175
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫‪users: [],‬‬
‫;}‬
‫‪},‬‬
‫{ ‪methods:‬‬
‫{ )( ‪getData: function‬‬
‫‪this.$http‬‬
‫)"‪.get("https://vue-remote-servers.firebaseio.com/users.json‬‬
‫{ >= )‪.then((response‬‬
‫;)(‪return response.json‬‬
‫)}‬
‫{ >= )‪.then((data‬‬
‫;][ = ‪const tmpArray‬‬

‫{ )‪for (let key in data‬‬


‫;]‪let withId = data[key‬‬
‫;‪withId.id = key‬‬
‫;)]‪tmpArray.push(data[key‬‬
‫}‬

‫;‪this.users = tmpArray‬‬
‫;)}‬
‫‪},‬‬
‫‪},‬‬
‫;}‬
‫>‪</script‬‬

‫>‪<style scoped‬‬
‫{‪.row‬‬
‫;‪margin-top:8px‬‬
‫}‬
‫>‪</style‬‬

‫مرة أخرى‪ ،‬ال تختلف الشيفرة البرمجية الموج ودة هن ا عن تل ك ال تي ك انت موج ودة في الفص ل الس ابق‪.‬‬

‫فعن دما ينتق ل المس تخدم له ذه الواجه ة وينق ر على ال زر ‪ ،Retrieve‬س تُ حدّ ث الواجه ة بحيث تع رض جمي ع‬

‫حاليا ضمن قاعدة بيانات ‪ .Firebase‬بعد عرض هؤالء المس تخدمين‪ ،‬س تالحظ وج ود‬
‫ً‬ ‫المستخدمين المخزنين‬

‫رابط اسمه ‪ Edit‬يظه ر بج وار ك ل مس تخدم‪ .‬عن د نق ر ه ذا ال زر س ينقلك التط بيق إلى واجه ة تحري ر بيان ات‬

‫المستخدم ‪ EditUser‬إلجراء التعديالت المطلوبة عليه‪.‬‬

‫ولكن السؤال هن ا‪ ،‬كي ف س تتمكّن الواجه ة ‪ EditUser‬من معرف ة المس تخدم المطل وب تحري ر بيانات ه؟‬
‫ّ‬
‫الجواب هو في المكوّ ن ‪ router-link‬الموجود ضمن هذه الواجهة ‪ .AllUsers‬انظ ر معي إلى االس تخدام‬

‫الجديد للمكوّ ن ‪:router-link‬‬

‫‪<router-link :to="{name: 'EditUser', params:‬‬


‫>‪{user:usr}}">Edit</router-link‬‬

‫‪176‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫استخدمنا في المقطع السابق السمة ‪ to‬بشكل مختل ف عن اس تخدامنا له ا في الواجه ات الس ابقة‪ .‬الح ظ‬

‫أواًل كيف وضعت نقطتين رأسيتين قبل كلمة ‪ to‬مباش ر ًة (وهم ا ض روريتان في ه ذه الحال ة)‪ ،‬ثم أس ندت إلى‬

‫السمة ‪ :to‬كائن وليس نص عادي كما فعلنا من قبل‪:‬‬

‫}}‪{name: 'EditUser', params: {user:usr‬‬

‫ّ‬
‫ويمثل اسم الواجهة التي نري د‬ ‫هذا الكائن لديه مفتاحين‪ .‬األول هو ‪ name‬ويمتلك القيمة '‪،'EditUser‬‬

‫االنتقال إليها عندما ينقر المستخدم ه ذا الراب ط‪ .‬الح ظ أ ّن ني قلت "اس م" وليس "مس ار"‪ ،‬أي أنّ االس م ال ذي‬
‫ً‬
‫موافق ا للقيم ة ‪ name‬ال تي ك انت موج ودة ض من مل ف المس ارات ‪ routes.js‬عن دما‬ ‫نكتبه هنا يحب أن يكون‬

‫عرفنا تلك المسارات إذا كنت تذكر‪.‬‬

‫بالنسبة للمفتاح الثاني فهو ‪ params‬وهو مسؤول عن تمري ر أي ة وس ائط نرغبه ا إلى الواجه ة ال تي نري د‬

‫االنتقال إليها‪ .‬في حالتنا ه ذه ن رغب بتمري ر بيان ات المس تخدم الم راد تحريره ا بش كل كام ل‪ .‬طريق ة التمري ر‬

‫مررنا كائن جديد للمفتاح ‪ params‬يحتوي ه ذا الك ائن في حالتن ا ه ذه‪ ،‬على مفت اح‬
‫مفيدة للغاية‪ ،‬الحظ كيف ّ‬
‫وحيد اسمه ‪ user‬ونسند له القيمة ‪ usr‬التي ُتعت بر كائ ًن ا مس تقاًل بح د ذات ه يحت وي على بيان ات المس تخدم‬

‫المراد تحرير بياناته‪ .‬أي أنّ {‪ }user:usr‬عب ارة عن ثنائي ة تح وي اس م الوس يط ‪ user‬م ع القيم ة المرتبطة‬

‫به ‪.usr‬‬

‫يمكن بالطبع تمرير أكثر من وسيط إلى الواجهة باألسلوب السابق‪ ،‬وذلك بوضعها بج وار بعض ها على ش كل‬

‫مفاتيح متعاقبة كما يلي‪:‬‬

‫}‪params: {key1:val1, key2:val2, ...‬‬

‫ستبدو الواجهة ‪ AllUsers‬على النحو التالي‪:‬‬

‫‪177‬‬
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

EditUser.vue ‫ الملف‬11.2.5

‫ باإلض افة إلى إمكاني ة ح ذف بيان ات ه ذا المس تخدم‬،‫في هذه الواجهة يمكنن ا تع ديل بيان ات المس تخدم‬
ً
.)‫مسبقا في الفصل السابق‬ ّ ‫بشكل‬
‫كلي من قاعدة البيانات (هذه ميزة جديدة لم تكن ُمضافة‬

:‫إليك الشيفرة البرمجية الخاصة بهذا الملف‬

<template>
<div class="container">
<div class="row">
<div class="col-6 offset-2">
<h1>Edit User</h1>
</div>
</div>
<div class="row">
<div class="col-6 offset-2">
<div class="container">
<div class="row">
<div class="col-12">
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" v-
model="user.username" />
</div>
<div class="form-group">
<label>First name</label>
<input type="text" class="form-control" v-
model="user.firstname" />
</div>
<div class="form-group">
<label>Last name</label>
<input type="text" class="form-control" v-
model="user.lastname" />
</div>
</div>
</div>
</div>
<button class="btn btn-primary float-left" style="margin-
left:12px;" @click="putUser()">Save</button>
<button
class="btn btn-danger float-right"
style="margin-right:12px;"
@click="deleteUser()"
>Delete</button>
</div>
</div>
</div>
</template>

178
Vue.js ‫أساسيات إطار العمل‬ ‫بناء تطبيقات ذات صفحة واحدة‬

<script>
export default {
name: "EditUser",
methods: {
putUser() {
this.$http
.put(
"https://vue-remote-servers.firebaseio.com/users/" +
this.$route.params.user.id +
".json",
this.user
)
.then(
() => {
this.$alert(
"The user is updated.",
"Operation Succeeded",
"success"
);
},
(error) => {
console.log(error);
}
);
},
deleteUser() {
this.$confirm("Are you sure you want to delete the
user?").then(() => {
this.$http
.delete(
"https://vue-remote-servers.firebaseio.com/users/" +
this.$route.params.user.id +
".json"
)
.then(
() => {
this.$router.push('AllUsers');
},
(error) => {
console.log(error);
}
);
});
},
},
computed: {
user() {
return this.$route.params.user;
},
},
};

179
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫>‪</script‬‬

‫>‪<style scoped‬‬
‫{ ‪.row‬‬
‫;‪margin-top: 8px‬‬
‫}‬
‫>‪</style‬‬

‫الشيء الذي أود التركيز عليه من الشيفرة السابقة ه و كيفي ة اس تالم الوس ائط المم ّررة له ذه الواجه ة من‬

‫ثم تعبئة هذه البيانات ضمن حقول البيانات‬


‫الواجهة الخاصة بعرض بيانات جميع المستخدمين ‪ AllUsers‬ومن ّ‬
‫المناسبة لها‪ ،‬ليتمكّن المستخدم من تعديلها إن أحب‪ .‬أنشأت لهذا الغرض خاص ية محس وبة اس مها ‪ user‬إلي ك‬

‫الشيفرة البرمجية الخاصة بها‪:‬‬

‫{ )(‪user‬‬
‫;‪return this.$route.params.user‬‬
‫}‬

‫مررن اه من‬
‫ترج ع الوس يط ال ذي ّ‬
‫تحت وي ه ذه الخاص ية المحس وبة على تعليم ة برمجي ة واح دة ُ‬

‫الواجهة ‪ AllUsers‬قبل قليل‪:‬‬

‫‪this.$route.params.user‬‬

‫الوسيط هو ‪ user‬كما أسميناه ض من الواجه ة ‪ ،AllUsers‬يحت وي ه ذا الوس يط على بيان ات المس تخدم‬

‫‪ username‬و ‪ firstname‬و ‪ lastname‬كم ا وردت من الواجه ة ‪ .AllUsers‬ه ذا الوس يط موج ود ض من‬

‫المفت اح ‪ params‬كم ا ه و واض ح‪ ،‬والموج ود ب دوره ض من الك ائن ‪( $route‬الموج ود ض من المكتب ة ‪vue-‬‬

‫‪ .)router‬وبما أنّ الخاصية ‪ user‬محسوبة فإنّ البيانات المستخلصة منها ستُ ّ‬


‫عمم مباشرة على الحقول الثالثة‬

‫الموجودة ضمن الواجهة ‪ EditUser‬كما يلي‪:‬‬

‫>"‪<div class="col-12‬‬
‫>"‪<div class="form-group‬‬
‫>‪<label>Username</label‬‬
‫‪<input type="text" class="form-control" v-‬‬
‫>‪model="user.username" /‬‬
‫>‪</div‬‬
‫>"‪<div class="form-group‬‬
‫>‪<label>First name</label‬‬
‫‪<input type="text" class="form-control" v-‬‬
‫>‪model="user.firstname" /‬‬
‫>‪</div‬‬
‫>"‪<div class="form-group‬‬
‫>‪<label>Last name</label‬‬
‫‪<input type="text" class="form-control" v-‬‬
‫>‪model="user.lastname" /‬‬

‫‪180‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫>‪</div‬‬
‫>‪</div‬‬

‫أود ً‬
‫أيض ا التح دث عن الت ابع ‪ $confirm‬الموج ود ض من المكتب ة ‪ vue-simple-alert‬ووظيفت ه‬

‫تخييرك بين أمرين‪ .‬استخدمت التابع ‪ $confirm‬ضمن التابع ‪ deleteUser‬وذلك لكي نطلب من المستخدم‬

‫تأكيد أ ّن ه يريد حذف المستخدم الحالي من قاعدة البيانات‪ .‬انظر الشيفرة البرمجية للتابع ‪:deleteUser‬‬

‫>= )((‪this.$confirm("Are you sure you want to delete the user?").then‬‬


‫{‬
‫‪this.$http‬‬
‫(‪.delete‬‬
‫‪"https://vue-remote-servers.firebaseio.com/users/" +‬‬
‫‪this.$route.params.user.id +‬‬
‫"‪".json‬‬
‫)‬
‫(‪.then‬‬
‫{ >= )(‬
‫;)'‪this.$router.push('AllUsers‬‬
‫‪},‬‬
‫{ >= )‪(error‬‬
‫;)‪console.log(error‬‬
‫}‬
‫;)‬
‫;)}‬

‫األول‪ .‬يحت وي‬


‫مرر لتابع ‪ّ then‬‬
‫الم ّ‬ ‫سي َّ‬
‫نفذ تابع السهم ُ‬ ‫في حال تمت الموافقة على حذف المستخدم الحالي‪ُ ،‬‬
‫تابع السهم هذا على الشيفرة البرمجي ة ال تي س تحذف بيان ات المس تخدم من قاع دة بيان ات ‪ Firebase‬وهي‬

‫مألوفة وتشبه أخواتها من المقاطع البرمجية األخرى المسؤولة عن التعامل مع قاعدة بيانات ‪ .Firebase‬الح ظ‬

‫برمجية أخرى وظيفتها تحويل المستخدم إلى الواجه ة ‪AllUsers‬‬


‫ّ‬ ‫أ ّننا ّ‬
‫ننفذ تابع ‪ then‬آخر يحتوي على تعليمة‬

‫بعد االنتهاء من حذف المستخدم الحالي‪.‬انظر لهذه التعليمة‪:‬‬

‫;)'‪this.$router.push('AllUsers‬‬

‫هذا هو األسلوب المتبع لالنتقال بين الواجهات بشكل برمجي‪ ،‬وذلك عن طريق اس تخدام الت ابع ‪ push‬من‬

‫نمر ر "اسم" الواجهة التي نرغب باالنتقال إليها كوسيط للتابع ‪.push‬‬
‫الكائن ‪ ،$router‬حيث ّ‬

‫‪181‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫بناء تطبيقات ذات صفحة واحدة‬

‫‪ 11.3‬ختام الفصل‬

‫كبيرا بعض الش يء‬


‫ً‬ ‫تناولنا في هذا الفصل مقدّ مة إلى التوجيه في ‪ .Vue.js‬في الحقيقة يعد هذا الموضوع‬

‫التوس ع في ه ذا الموض وع‬


‫ّ‬ ‫وله جوانب متعدّ دة غطين ا في ه ذا الفص ل األساس ي منه ا‪ .‬في ح ال احتجت إلى‬

‫مس تقباًل ‪ ،‬فأعتق د أنّ خ ير رفي ق ل ك س يكون التوثي ق الرس مي للتوجي ه تج ده على ه ذا الموقع ولكن باللغ ة‬

‫اإلنجليزية‪ .‬أقترح عليك بعد قراءة هذا الفصل أن تحاول تج ريب بن اء تطبيق ات بس يطة تعتم د على التوجي ه‪،‬‬

‫لكي تعتاد على هذه التقنية‪.‬‬

‫‪182‬‬
‫‪ .12‬نرش تطبيق ‪ Vue.js‬إىل اإلنتـرنت‬

‫ّ‬
‫سنتعلم في هذا الفصل‪:‬‬

‫تجهيز التطبيق قبل النشر‬ ‫•‬

‫إنشاء موقع جديد على منصة ‪ Netlify‬باالستناد إلى مستودع ‪GitHub‬‬ ‫•‬

‫هذا الفصل هو األخير‪ ،‬حيث سنتوّ ج هذا الكتاب بشرح كيفية نشر التطبيق الوارد في الفصل الس ابق‪ ،‬وه و‬

‫عبارة عن تطبيق ‪ SPA‬يدعم التخاطب مع قاعدة بيانات موجودة على ‪ Firebase‬كما تذكر‪ .‬بع د بن اء التط بيق‬

‫ونشره‪ ،‬س يتمكّن أي مس تخدم ح ول الع الم من الوص ول إلى تطبيقن ا ه ذا والتفاع ل مع ه‪ .‬س أعتمد اس تخدام‬

‫مخصص ترغب به‪.‬‬


‫ّ‬ ‫منصة أو خادوم‬
‫المنصة المجانية ‪ Netlify‬لهذا الغرض‪ ،‬ويمكنك بالطبع استخدام أي ّ‬
‫ّ‬

‫‪ 12.1‬تجهزي التطبيق قبل النرش‬

‫كما ذكرت قبل قليل‪ ،‬سنعتمد التطبيق المبني في الفصل السابق لتشغيله على خدم ة ‪ .Netlify‬بغي ة ذل ك‪،‬‬

‫الي‪:‬‬ ‫تودع ‪ Github‬الت‬ ‫ل على مس‬ ‫كل كام‬ ‫بيق بش‬ ‫ذا التط‬ ‫عه‬ ‫عملت على رف‬
‫‪github.com/HsoubAcademy/vuejs-spa‬‬

‫سبب ذلك هو أنّ ‪ Netlify‬يسحب التطبيقات الموجودة على خدمات ‪ Git‬مثل ‪ .GitHub‬س تحتاج ب الطبع‬

‫إلى أن يكون لديك حساب على ‪ُ .GitHub‬زر المستودع الس ابق‪ ،‬وس ّجل دخول ك على ‪ GitHub‬إذا لم تكن ق د‬
‫ً‬
‫مسبقا‪ ،‬ومن الصفحة الرئيسية لمستودعنا‪ ،‬انقر الزر ‪ Fork‬في الزاوي ة اليم نى العلي ا إلنش اء تفريع ة‬ ‫فعلت ذلك‬

‫جديدة من الشيفرة البرمجية الحالية‪.‬‬


‫أساسيات إطار العمل ‪Vue.js‬‬ ‫نرش تطبيق ‪ Vue.js‬إىل اإلنتـرنت‬

‫سيتطلب األمر لحظات حتى يتم إنشاء التفريعة من الشيفرة البرمجية ووضعها ضمن حسابك الخاص‪.‬‬

‫‪ 12.2‬نرش التطبيق عىل منصة ‪Netlify‬‬

‫أنشئ حسابًا جديدً ا على ‪ Netlify‬عن طريق زيارة الصفحة الرئيسية له الموقع الرسمي‪ .‬انقر ‪ Sign up‬من‬

‫حالي ا على‬
‫ً‬ ‫الزاوي ة اليم نى العلي ا لتظه ر ل ك الص فحة الخاص ة بإنش اء اش تراك جدي د‪ .‬تب دو ه ذه الص فحة‬

‫النحو التالي‪:‬‬

‫الح ظ معي وج ود خي ارات متع ددة للتس جيل‪ .‬سنس تخدم في حالتن ا ه ذه التس جيل باس تخدام البري د‬

‫اإللكتروني‪ .‬انقر الخيار األخير "‪."Email‬‬

‫بعد كتابة البريد اإللكتروني الذي تودّ التسجيل عن طريقه‪ ،‬سيرس ل الموق ع رس الة تحق ق إلى ه ذا البري د‬

‫المرس ل إلى بري دك‪ ،‬وبع د تس جيل ال دخول‬


‫َ‬ ‫تحت وي على راب ط لتفعي ل الحس اب الجدي د‪ ،‬انق ر ذل ك الراب ط‬

‫إلى ‪ Netlify‬باستخدام حسابك الجديد ستظهر صفحة مشابهة لما يلي‪:‬‬

‫‪184‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫نرش تطبيق ‪ Vue.js‬إىل اإلنتـرنت‬

‫ه ذه هي الص فحة الرئيس ية وال تي تحت وي على جمي ع عناص ر التحكم الخاص ة بحس ابك‪ .‬نحن اآلن‬
‫ً‬
‫مسبقا‪ ،‬وهي فارغة اآلن بطبيعة الح ال‪ .‬انق ر‬ ‫موجودون ضمن الصفحة الخاصة بمواقع الويب التي أنشأتها أنت‬

‫الزر "‪ "New site from Git‬من الطرف األيمن في األعلى‪ ،‬لالتصال بمستودع من النوع ‪ .Git‬ستحص ل على‬

‫شكل شبيه بما يلي‪:‬‬

‫انق ر ال زر ‪ GitHub‬الموج ود في األس فل‪ ،‬س يؤدي ذل ك إلى فتح ناف ذة جدي دة ق د تطلب من ك تس جيل‬

‫الدخول بحسابك على ‪ GitHub‬في حال لم تكن مسجاًل دخولك على ‪ GitHub‬بعد (ذلك الحساب الذي أنش أت‬

‫التفريعة ضمنه)‪:‬‬

‫‪185‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫نرش تطبيق ‪ Vue.js‬إىل اإلنتـرنت‬

‫انقر الزر ‪ Authorize Netlify‬للسماح لـ ‪ Netlify‬بالوصول إلى حساب ‪ GitHub‬الخ اص ب ك‪ .‬بع د إكم ال‬

‫عملية االستيثاق (‪ ،)Authorization‬ستظهر لك ص فحة تس مح ل ك باالختي ار بين الس ماح الكام ل لـ ‪Netlify‬‬

‫للوصول إلى جميع المستودعات ضمن حسابك أو السماح بالوصول إلى مستودع مح دّ د‪ .‬بالنس بة إلي‪ ،‬سأس مح‬

‫ل ه بالوص ول إلى المس تودع ‪ vuejs-spa‬فق ط‪ ،‬وه و المس تودع ال ذي أنش أناه كتفريع ة قب ل قلي ل من‬

‫الفقرة السابقة‪.‬‬

‫‪186‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫نرش تطبيق ‪ Vue.js‬إىل اإلنتـرنت‬

‫تتضمن االنتقال إلى موقع ‪ GitHub‬بشكل تلق ائي وذل ك إلكم ال‬
‫ّ‬ ‫انقر زر ‪ Install‬لتبدأ عملية التثبيت التي‬

‫عملية الربط (قد يطلب منك كلمة المرور لحسابك في ‪ GitHub‬مرة أخرى)‪.‬‬

‫بعد االنتهاء من العملية السابقة ستحتاج إلى العودة إلى الصفحة الرئيس ية لموق ع ‪ Netlify‬وتك رار عملي ة‬

‫إنشاء موقع من مستودع ‪( GitHub‬الشكل رقم ‪ .)3‬هذه المرة س يظهر لن ا المس تودع ال ذي ن رغب باس تيراده‪.‬‬

‫انظر إلى الشكل التالي‪:‬‬

‫‪187‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫نرش تطبيق ‪ Vue.js‬إىل اإلنتـرنت‬

‫الح ظ معي كي ف ظه ر المس تودع ‪ vuejs-spa‬م ع اس م حس اب ‪ GitHub‬الت ابع لي ‪.husamburhan‬‬


‫س أختار ه ذا المس تودع ب النقر علي ه‪ ،‬س يؤدي ذل ك إلى الخط وة النهائي ة وال تي ّ‬
‫تتمث ل بتحدي د الف رع‬

‫الرئيسي (‪ )Branch‬التي سنستخدمه في عملية نش ر التط بيق‪ ،‬باإلض افة إلى تحدي د التعليم ة الخاص ة ببن اء‬
‫ّ‬
‫والمجل د الخاص بالنشر‪ .‬احرص على أن تحصل على شكل شبيه بما يلي‪:‬‬ ‫التطبيق‪،‬‬

‫أخيرا‪ ،‬انقر الزر ‪ Deploy site‬لبناء التطبيق ونشر الموقع على اإلن ترنت‪ .‬س تأخذ ه ذه العملي ة القلي ل من‬
‫ً‬
‫الوقت‪ ،‬حيث ستنتقل إلى ص فحة تخ برك ب أنّ عملي ة البن اء والنش ر قي د التجه يز‪ .‬بع د أن تنتهي عملي ة البن اء‬

‫والنشر ستحصل على رابط حي للصفحة تستطيع مشاركته مع من ترغب‪.‬‬

‫‪188‬‬
‫أساسيات إطار العمل ‪Vue.js‬‬ ‫نرش تطبيق ‪ Vue.js‬إىل اإلنتـرنت‬

‫بالنسبة لي حصلت على الرابط التالي‪suspicious-mahavira-10e9ac.netlify.app :‬‬

‫منطقي ا أك ثر‪ .‬كم ا‬


‫ً‬ ‫الحظ أنّ اسم الموقع يُ َّ‬
‫ولد بشكل عشوائي‪ .‬يمكنك بالطبع تغيير هذا االسم بحيث يصبح‬

‫مخص ص مث ل ‪ www.example.com‬وربط ه م ع ه ذا الموق ع إن أحببت‪ .‬جمي ع‬


‫ّ‬ ‫يمكن ك إض افة اس م نط اق‬

‫الخيارات السابقة وأكثر يمكن التحكم بها من خالل خيارات الموقع في ‪.Netlify‬‬

‫‪ 12.3‬ختام الفصل‬

‫ختمنا في هذا الفصل كتابنا ه ذا‪ ،‬أساس يات إط ار العم ل ‪ ،Vue.js‬بش رح كيفي ة نش ر التط بيق إلى خدم ة‬

‫متاحا على اإلنترنت‪ .‬توجد العديد من الخ دمات ال تي تس مح بالنش ر المج اني‪ ،‬ولك ّن ني‬
‫ً‬ ‫مجانية ليصبح تطبيقنا‬

‫اخترت أبسطها وهي ‪ Netlify‬حسب رأيي‪.‬‬

‫ّ‬
‫لتتعلم ه‬ ‫حاولت في هذا الكتاب تغطية األمور األساسية فقط في ‪ ،Vue.js‬لذلك فأمامك الكثير بك ل تأكي د‬

‫حول هذه التقنية الواعدة‪ .‬يمكنك زيارة الموقع الرسمي إلطار العمل ‪ Vue.js‬واالستزادة حوله‪.‬‬

‫‪189‬‬
‫أحدث إصدارات أكاديمية حسوب‬

You might also like