سنتعلّم في هذا الدرس:
- فهم قوالب Vue.js
- الوصول إلى البيانات والتوابع من كائنات Vue.js
- الربط مع السمات Attributes
- كتابة شيفرة HTML خام
- التعامل مع الأحداث Events
- استخدام الربط ثنائي الاتجاه
نتابع عملنا في هذا الدرس وهو الدرس الثاني من سلسلة دروس تعلّم Vue.js. سنتعلم هذه المرّة كيفية الوصول والتعامل مع DOM، حيث سنتعلّم كيف نستخدم موجّهات Vue.js مختلفة للوصول إلى بيانات كائن Vue.js والتفاعل معها، وسنتوسّع في التعامل مع الأحداث events بالإضافة إلى كيفية استخدام الربط ثنائي الاتجاه مع العناصر.
فهم قوالب Vue.js
تعاملنا في الدرس السابق مع تطبيقات استخدمت مزايا بسيطة من Vue.js، وإذا كنت تذكر أنّنا قد كتبنا شيفرة HTML بسيطة ومن ثمّ استخدمنا الاستبدال النصي '{{ message }}' ، واستخدمنا أيضًا موجّه 'v-on' للاستجابة إلى دخل المستخدم. ما يقوم به Vue.js من وراء الكواليس، هو أخذ نسخة عن شيفرة HTML وحفظها داخليًّا على شكل قالب template، بعد ذلك يتم إجراء عملية تصيير (rendering) على نسخة من القالب السابق، باستخدام الموجّهات وتعابير الاستبدال النصي (في حال وجودها ضمن القالب)، ثمّ بعد الانتهاء من التصيير يتم عرض الخرج النهائي على المستخدم.
الآن، وعندما يُحدَث أي تغيير جديد في قيمة أي حقل مرتبط من كائن Vue.js، ستُجرى عملية تصيير جديدة على نسخة جديدة من القالب السابق المخزّن داخليًّا، ثم يُعرض الخرج النهائي من جديد على المستخدم.
أي كما لو أنّنا أنشأنا ارتباطًا دائمًا بين كائن Vue.js وبين شيفرة HTML. وهذا ما رأيناه فعليًا في التطبيقات البسيطة التي تناولناها في الدرس السابق. ملاحظة من باب التذكير، نستخدم في هذه السلسلة الموقع jsfiddle.net بشكل افتراضي لتشغيل جميع التطبيقات التي نكتبها. ونحتاج بالطبع إلى إدراج ملف إطار العمل Vue.js لكي نستطيع تنفيذ هذه التطبيقات. إذا أردت أن تعرف كيف ذلك، يمكنك العودة إلى الدرس السابق.
الوصول إلى البيانات والتوابع من كائنات Vue.js
انظر إلى المثال التالي (مثل العادة، أول مقطع يمثّل شيفرة HTML وثاني مقطع يمثّل شيفرة JavaScript):
<div id="app"> {{ title }} </div>
var app = new Vue({ el: '#app', data: { title: 'Hello Vue!' } })
عندما نستخدم الاستبدال النصي '{{ 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()
ضمن الحاضنة المزدوجة {{ displayMessage() }}
ومرّة أخرى لم نستخدم الكلمة this
قبل اسم التابع. أي تابع يُكتب بهذه الطريقة سيُعتَبر افتراضيًّا على أنّه تابع موجود ضمن القسم methods
من كائن Vue.js. ولكن هذا السلوك الافتراضي لا يسري على شيفرة JavaScript الموجودة ضمن كائن Vue.js حيث يجب استخدام الكلمة this
في كلّ مرّة أردنا فيها الوصول إلى أحد أعضاء كائن Vue.js.
الربط مع السمات Attributes
لا يمكن استخدام تقنية الاستبدال النصي (باستخدام الحاضنة المزدوجة) لإدراج قيم ضمن سمات العناصر. لفهم هذا الموضوع بشكل جيّد، انظر معي إلى المثال التالي:
<div id="app"> <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
كنص مجرّد، يمكنك ملاحظة الرابط الناتج بعد النقر: https://fiddle.jshell.net/_display/{{title}} الحل لهذه المشكلة بسيط، ويتمثّل في تجنّب استخدام السمة 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 على الرابط رابط للاطلاع على جميع الموجّهات المتوفّرة.
كتابة شيفرة 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. ولكن عند التنفيذ لن تحصل على ما هو متوقع، ستحصل على الخرج التالي:
First Item Second Item Third Item
أي أنّك ستحصل على نص عادي دون أن يتعرّف عليه المتصفّح على أنّه شيفرة HTML. يمكن هذه المشكلة بسهولة بإجراء تعديل على شيفرة HTML فقط على النحو التالي:
<div id="app"> <p v-html='raw'> </p> </div>
التعديل الذي أجريته هو استخدام الموجّه v-html
الذي يسمح في حالتنا هذه باستخدام محتويات الحقل raw
كشيفرة HTML نظامية وليس مجرّد نص عادي (لاحظ أنّني قد تخلصت من الاستبدال النصّي {{ raw }}
. أعد تنفيذ التطبيق، لتحصل على قائمة مُنسّقة بشكل صحيح.
التعامل مع الأحداث (Events)
للأحدث كما نعلم أهميّة عظيمة في تطوير التطبيقات التي تتفاعل مع المستخدم. تدعم Vue.js الأحداث بشكل جيّد، ولقد تعاملنا في الدرس السابق مع نوعين من الأحداث: حدث الإدخال ضمن مربّع النص، وحدث النقر بزر الفأرة على عنصر HTML. سنتناول في هذا الدرس الأحداث بشيء من التفصيل، حيث سنتعلّم كيف ننصت إلى أحداث الفأرة، بالإضافة إلى الإنصات إلى أحداث لوحة المفاتيح.
الإنصات إلى أحداث الفأرة
نبدأ بالتعامل مع أحداث الفأرة، لتُنعش ذاكرتك، انظر معي إلى التطبيق البسيط الموجود في الدرس السابق:
<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
في المثال السابق) سيتم توليد كائن يحتوي على معلومات مهمّة حول الحدث الذي وقع، ويتم تمرير هذا الكائن بشكل تلقائي إلى التابع المعالج للحدث.
على العموم، يمكن الاستغناء عن هذا السلوك التلقائي، وتمرير قيمة كيفيّة للتابع المعالج للحدث. انظر معي إلى المثال التالي:
<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 فسيصبح على النحو التالي:
increase: function(value, event){ this.counter += value; }
أي مجرّد إضافة وسيط آخر.
التعديل على كيفية الاستجابة للأحداث
نحتاج في بعض الأحيان أن نُعدّل على كيفيّة الاستجابة للأحداث، فربما نحتاج في وقت ما إلى إيقاف الاستجابة لحدث ما لأحد العناصر دونًا عن العناصر الأخرى في 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. لاحظ معي أيضًا كيف نحصل على الإحداثيات الحالية لمؤشّر الفأرة (الفاصلة 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()
ثم أعد تنفيذ التطبيق مرّة أخرى، لترى كيف أنّ الإحداثيات ستتغيّر عندما يمر مؤشّر الفأرة فوق عنصر span
هذه المرة. يمكن استخدام صيغة أبسط للتعديل على الأحداث، فمن الممكن حذف التابع uncoveredArea
بالكامل من قسم methods
، والاكتفاء بالقسم الخاص بالموجّه على النحو التالي:
v-on:mousemove.stop=''
أي أنّنا قد استغنينا عن الشيفرة اللازمة لإيقاف انتشار الحدث mousemove
. نسمي .stop
هنا بمعدِّل الحدث (event modifiers). هناك عدّة معدّلات أحداث مفيدة سنستعرض بعضها منها خلال مسيرتنا في هذه السلسلة.
الإنصات إلى أحداث لوحة المفاتيح
يمكننا أحيانًا أن نحتاج إلى الإنصات أيضًا إلى الأحداث الناشئة من لوحة المفاتيح. والأسلوب المتبع هنا، يشبه إلى حدّ كبير ما كنّا نفعله مع أحداث الفأرة. إذا أردنا مثلًا الإنصات إلى حدث تحرير مفتاح من لوحة المفاتيح يمكن أن نستخدم الوسيط 'keyup' للموجّه v-on
على النحو التالي:
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' ضمن الواجهة. ولكن دعنا نتساءل، ماذا لو أردنا أن يستجيب المعالج 'keyIsUp' كلّما حُرِّر مفتاح المسافة (space) فقط، وليس عند أيّ مفتاح يُحرِّره المستخدم. الجواب ببساطة، هو في استخدام معدّل الحدث '.space' بعد 'keyup'. أضف فقط الكلمة '.space' إلى 'keyup' إلى المثال السابق. أي على النحو التالي:
v-on:keyup.space = 'keyIsUp'
أعد تنفيذ التطبيق لترى أنّ محتويات عنصر 'p' أصبحت لا تُحدَّث إلّا بعد تحرير المفتاح space. يوجد بالطبع العديد من المعدّلات التي تمثّل جميع المفاتيح على لوحة المفاتيح، فهناك مثلًا 'enter' و 'tab' و 'up' لمفتاح السهم العلوي، و 'down' لمفتاح السهم السفلي وهكذا. لمعدّلات أحداث لوحة المفاتيح الكثير من الفوائد، يتمثّل أبسطها في إرسال المحتوى الذي أدخله المستخدم بمجرد ضغطه للمفتاح Enter، أو إرسال البيانات مباشرةً بينما يكتبها المستخدم للحصول على مقترحات أثناء عملية الكتابة (كما يفعل محرّك البحث غوغل أثناء كتابة المستخدم للمفردات المراد البحث عنها). وغيرها الكثير من الاستخدامات.
استخدام الربط ثنائي الاتجاه
في معظم الأمثلة السابقة عمدنا إلى استخدام ربط باتجاه واحد، من الشيفرة إلى عنصر HTML. وفي بعض الحالات استطعنا أن نعكس هذا الأمر. أي استطعنا تعديل قيمة الحقل عن طريق الانصات إلى حدث الإدخال v-on:input وبالتالي معالج حدث مخصّص لهذه الغاية. ولكن توجد طريقة مباشرة وسهلة لإيجاد ربط ثنائي الاتجاه فعلي في Vue.js وذلك باستخدام الموجّه v-model وبدون الحاجة إلى معالج حدث، كمال في المثال التالي:
<div id="app"> <input type='text' v-model='name'/> {{ name }} </div>
var app = new Vue({ el: '#app', data: { name: 'Hello Vue!' } })
التطبيق السابق بسيط، وهو يعمل على إجراء ربط ثنائي الاتجاه بين الحقل name وبين عنصر مربّع النص، أي سيكون هناك ارتباط آني بين الحقل name وبين عنصر مربّع النص، فإذا حدث تغيّر لأحدهما سينعكس مباشرةً على الآخر. نفّذ التطبيق السابق وسترى مباشرةً الخرج التالي:
لاحظ كيف أنّ محتوى مربّع النص قد تمّت تعبئته تلقائيًا بقيمة الحقل name، وبالمثل إذا حاولت الآن كتابة أي شيء ضمن مربّع النص سيتم تعديل قيمة الحقل name فورًا وفقًا له، وبالتالي سيُعدّل محتوى النص الموجود في الطرف الأيمن بسبب وجود الاستبدال النصي {{name}}.
ختامًا
تعلّمنا في هذا الدرس كيفيّة التعامل مع DOM، حيث تحدثنا عن القوالب، وكيف نصل إلى البيانات والتوابع بشيء من التفصيل، كما تعلّمنا كيفيّة الربط مع السمات، والتعامل مع الأحداث، وتعلّمنا كيفية الربط ثنائي الاتجاه الذي يسمح لنا بمزامنة البيانات بالاتجاهين.
تمارين داعمة
تمرين 1
يُطلب في هذا التمرين تطوير تطبيق الآلة الحاسبة البسيط الذي بنيناه في الدرس السابق. بحيث يسمح التطبيق الجديد بإجراء العمليات الحسابية الأربع بدلًا من عملية الجمع الوحيدة التي كان يدعمها التطبيق السابق. أقترح الواجهة التالية للتطبيق:
لاحظ أنّني قد استخدمت عنصر 'select'، يمكنك استخدام أي طريقة أخرى لاختيار العمليات الحسابية الأربع. من الضروري أن يُوجِد التطبيق الناتج النهائي إذا حدث أحد الأمرين التاليين:
- تغيير قيمة أحد المعاملين على طرفي العملية الحسابية.
- تغيير العملية الحسابية عن طريق القائمة المنسدلة.
أرجو أن يتمكن التطبيق من تمييز حالة القسمة على صفر، وإظهار رسالة مناسبة للمستخدم.
تمرين 2
يُطلب في هذا التمرين إنشاء تطبيق يقدّم للمستخدم مقترحات نصيّة بينما يكتب المستخدم ضمن مربّع نص. سنحاكي عملية الاتصال مع خادوم بعيد عن طريق استخدام مصفوفة نصيّة ضمن الشيفرة. وسأعتبر أنّ المستخدم يحاول أن يُدخل اسم دولة عربية، فتظهر قائمة المقترحات بالأسفل بينما تتم عملية الإدخال. كما في الشكل التالي:
ستحتاج بالطبع إلى التعامل مع أحداث لوحة المفاتيح. ولكي يكون الأمر أكثر سهولة بالنسبة إليك. يمكنك استخدام المصفوفة الجاهزة التالية كمصدر للبيانات التي يُفترَض أن تكون قادمة من الخادوم:
[ 'السعودية', 'البحرين', 'مصر', 'السودان', 'ليبيا', 'الجزائر', 'المغرب', 'تونس', 'موريتانيا', 'العراق', 'سوريا', 'لبنان', 'قطر', 'الإمارات', 'الصومال', 'جزر القمر', 'الكويت', 'سلطنة عُمان', 'الأردن', 'اليمن', 'فلسطين' ]
اقرأ أيضًا
- المقال التالي: الموجهات الشرطية والتكرارية في Vue.js
- المقال السابق: مقدمة إلى Vue.js
- النسخة الكاملة لكتاب أساسيات إطار العمل Vue.js
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.