شرح مفهوم الأحداث والتوابع والنماذج في Vue.js


شرح مفهوم الأحداث والتوابع والنماذج في Vue.js August 30, 2022 at 10:02PM

لدينا الآن عينة من البيانات وحلقة تأخذ كل جزء من هذه البيانات وتصيّره أو تعرضه Render ضمن المكوِّن ToDoItem في تطبيقنا، كما نريد السماح لمستخدِمينا بإدخال عناصر مهامهم في التطبيق، لذلك سنحتاج إلى نص إدخال <input> وحدث يُطلَق عند إرسال البيانات وتابع لإطلاق الإرسال بهدف إضافة البيانات وتصيير القائمة ونموذج للتحكم في البيانات، وهذا هو موضوعنا في هذا المقال.

  • المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript، ومعرفة استخدام سطر الأوامر أو الطرفية، إذ تُكتَب مكوّنات Vue بوصفها مجموعةً من كائنات جافاسكربت التي تدير بيانات التطبيق وصيغة القوالب المستنِدة إلى لغة HTML المرتبطة مع بنية DOM الأساسية، وستحتاج إلى طرفية مثبَّت عليها node و npm لتثبيت Vue ولاستخدام بعض ميزاته الأكثر تقدمًا مثل مكوّنات الملف المفرد Single File Components أو دوال التصيير Render.
  • الهدف: تعلّم كيفية التعامل مع الاستمارات Forms في Vue والأحداث Events والنماذج Models والتوابع Methods المرتبطة بها.

إنشاء استمارة مهام جديدة

لدينا الآن تطبيق يعرض قائمةً بعناصر المهام، ولكن لا يمكننا تعديل قائمة العناصر دون تغيير شيفرتنا البرمجية يدويًا، إذًا لنصلح ذلك ولْننشئ مكوِّنًا جديدًا يسمح لنا بإضافة عنصر مهام جديد.

أنشئ ملفًا جديدًا يسمى ToDoForm.vue في مجلد المكوّنات، ثم أضِف عنصر <template> فارغًا ووسم <script> كما يلي:

<template></template>

<script>
  export default {};
</script>

أضِف استمارة HTML الذي يتيح إدخال عنصر مهام جديد وإرساله إلى التطبيق، كما نحتاج إلى العنصر <form> مع العناصر <label> و<input> و<button>، لذا عدِّل قالبك ليبدو كما يلي:

<template>
  <form>
    <label for="new-todo-input">
      What needs to be done?
    </label>
    <input
      type="text"
      id="new-todo-input"
      name="new-todo"
      autocomplete="off"
    />
    <button type="submit">
      Add
    </button>
  </form>
</template>

لدينا الآن مكوِّن استمارة يمكننا من خلاله إدخال عنوان عنصر مهام جديد والذي سيصبح عنوانًا أو تسمية label للمكوِّن ToDoItem المقابل عند تصييره لاحقًا، ولنحمِّل هذا المكوِّن في تطبيقنا، لذا ارجع إلى الملف App.vue وأضف تعليمة الاستيراد import التالية بعد تعليمة الاستيراد السابقة مباشرةً ضمن العنصر <script>:

import ToDoForm from './components/ToDoForm';

يجب تسجيل المكوِّن الجديد في المكون App من خلال تعديل الخاصية components الخاصة بكائن المكوِّن بحيث تبدو كما يلي:

components: {
  ToDoItem,
  ToDoForm
}

صيّر المكوِّن ToDoForm ضمن تطبيقك من خلال إضافة العنصر <to-do-form /‎> إلى العنصر <template> ضمن المكوِّن App كما يلي:

<template>
  <div id="app">
    <h1>My To-Do List</h1>
    <to-do-form></to-do-form>
    <ul>
      <li v-for="item in ToDoItems" :key="item.id">
        <to-do-item :label="item.label" :done="item.done" :id="item.id"></to-do-item>
      </li>
    </ul>
  </div>
</template>

إذا شغّلت موقعك الآن، فيجب أن ترى الاستمارة الجديدة كما يلي:

01_rendered-form-with-text-input.png

إذا ملأت الاستمارة ونقرت على زر "الإضافة Add"، فستسرسل الصفحة الاستمارة مرةً أخرى إلى الخادم، ولكننا لا نريد ذلك، وإنما نريد تشغيل تابع على الحدث submit الذي سيضيف المهام الجديدة إلى قائمة بيانات ToDoItem المُعرَّفة ضمن المكوِّن App، لذلك يجب إضافة تابع إلى نسخة المكوِّن.

إنشاء تابع وربطه بحدث باستخدام الموجه v-on

يمكن إتاحة تابع للمكوِّن ToDoForm من خلال إضافته إلى كائن المكوِّن ضمن الخاصية methods التي تشبه الخاصيات data()‎ و props وما إلى ذلك، إذ تحتوي الخاصية methods على أيّ تابع قد نحتاجه لاستدعاء مكوننا. كما تُشغَّل جميع التوابع عند استدعاء هذه الخاصية، لذلك لا يُعَدّ استخدامها لعرض المعلومات ضمن القالب أمرًا جيدًا، وبالتالي يجب استخدام الخاصية computed -التي سنتحدث عنها لاحقًا- لعرض البيانات الناتجة عن العمليات الحسابية.

يجب إضافة التابع onSubmit()‎ إلى الخاصية methods ضمن كائن المكوِّن ToDoForm، إذ سنستخدِم هذا التابع للتعامل مع إجراء الإرسال، لذا أضف هذا التابع كما يلي:

export default {
   methods: {
       onSubmit() {
          console.log('form submitted')
       }
   }
}

يجب بعد ذلك ربط التابع بمعالج حدث الإرسال submit للعنصر <form>، ويشبه ذلك إلى حد كبير كيفية استخدام إطار العمل Vue لصيغة v-bind بهدف ربط السمات، إذ يمتلك Vue موجِّهًا خاصًا لمعالجة الأحداث وهو v-on الذي يعمل باستخدام الصيغة v-on:event="method"‎، كما توجد صيغة مختصرة هي ‎@event="method"‎، إذ سنستخدِم الصيغة المختصرة في هذا المقال، وبالتالي أضف معالج حدث الإرسال submit إلى العنصر <form> كما يلي:

<form @submit="onSubmit">

إذا شغّلنا معالج حدث الإرسال، فلا يزال التطبيق يرسل البيانات إلى الخادم مما يتسبب في تحديث الصفحة، وبما أننا نطبّق كل عمليات المعالجة على العميل، فلا يوجد خادم ليتعامل مع إعادة الإرسال وسنفقد جميع الحالات المحلية عند تحديث الصفحة، إذ يمكن منع المتصفح من الإرسال إلى الخادم من خلال إيقاف إجراء الحدث الافتراضي أثناء انتشاره للأعلى Bubbling Up في الصفحة باستخدام التابع Event.preventDefault()‎ في لغة جافاسكربت الصرفة Vanilla JavaScript مثلًا. كما يحتوي إطار عمل Vue على صيغة خاصة تُسمَّى معدِّلات الأحداث Event Modifiers التي يمكنها معالجة هذا الأمر مباشرةً في قالبنا، في حين تُضاف المُعدِّلات إلى نهاية الحدث مع نقطة بالصورة ‎@event.modifier، ونوضِّح فيما يلي قائمةً بمعدِّلات الأحداث:

  1. ‎.stop: يوقِف انتشار الحدث ويكافئ التابع Event.stopPropagation()‎ في أحداث جافاسكربت العادية.
  2. ‎.prevent: يمنع سلوك الحدث الافتراضي ويكافئ التابع Event.preventDefault()‎.
  3. ‎.self: يؤدي إلى تشغيل المعالج فقط إذا أُرسِل الحدث من هذا العنصر المُحدَّد.
  4. {‎.key}: يؤدي إلى تشغيل معالج الأحداث باستخدام مفتاح محدَّد فقط، ويحتوي موقع MDN على قائمة بقيم المفاتيح الصالحة، ولكن يجب تحويل المفاتيح متعددة الكلمات إلى حالة الأحرف التي تسمى نمط أسياخ الشواء Kebab Case مثل page-down.
  5. ‎.native: يستمع إلى الحدث الأصيل Native في عنصر الجذر أو الغلاف الخارجي Outer-most Wrapping لمكوِّنك.
  6. ‎.once: يستمع إلى الحدث حتى تشغيله لمرة واحدة فقط لا أكثر.
  7. ‎.left: يشغِّل المعالج باستخدام حدث زر الفأرة الأيسر فقط.
  8. ‎.right: يشغِّل المعالج باستخدام حدث زر الفأرة الأيمن فقط.
  9. ‎.middle: يشغِّل المعالج باستخدام حدث زر الفأرة الأوسط فقط.
  10. ‎.passive: يكافئ استخدام المعامِل { passive: true } عند إنشاء مستمع حدث في لغة جافاسكربت الصرفة Vanilla JavaScript باستخدام التابع addEventListener()‎.

سنستخدِم في حالتنا المعالِج ‎.prevent لإيقاف إجراء الإرسال الافتراضي للمتصفح، لذا أضِف ‎.prevent إلى معالج الإرسال ‎@submit في قالبك كما يلي:

<form @submit.prevent="onSubmit">

إذا حاولت إرسال الاستمارة الآن، فستلاحظ عدم إعادة تحميل الصفحة، وإذا فتحت الطرفية، فيمكنك رؤية نتائج التابع console.log()‎ التي أضفناها ضمن التابع onSubmit()‎.

ربط البيانات مع الدخل باستخدام الموجه v-model

نحتاج الآن إلى طريقة للحصول على القيمة الموجودة في حقل الإدخال <input> من الاستمارة لنتمكّن من إضافة عنصر المهام الجديد إلى قائمة بيانات ToDoItems، إذ يكون أول شيء نحتاجه هو الخاصية data في استمارتنا لتعقّب قيمة المهمة.

أضِف التابع data()‎ إلى كائن مكون ToDoForm الذي يعيد الحقل label، إذ يمكننا ضبط قيمة الحقل label الأولية بوصفها سلسلة نصية فارغة، ويجب أن يبدو كائن المكوِّن الآن كما يلي:

export default {
  methods: {
    onSubmit() {
      console.log("form submitted");
    }
  },
  data() {
    return {
      label: ""
    };
  }
};

نحتاج الآن إلى طريقة ما لربط قيمة حقل الإدخال <input> ذي المعرِّف new-todo-input بالحقل label، إذ يمتلك إطار العمل Vue موجِّهًا خاصًا لذلك وهو v-model الذي يرتبط مع خاصية البيانات التي ضبطتها عليه ويبقيها متزامنةً مع حقل الإدخال <input>، في حين يعمل الموجّه v-model مع جميع أنواع حقول الإدخال المختلفة بما في ذلك مربعات الاختيار Checkboxes وأزرار الانتقاء Radios وحقول الاختيار Select Inputs، كما يُستخدَم هذا الموجِّه من خلال إضافة سمة بالصورة v-model="variable"‎ إلى العنصر <input> كما يلي:

<input
  type="text"
  id="new-todo-input"
  name="new-todo"
  autocomplete="off"
  v-model="label" />

ملاحظة: يمكنك مزامنة البيانات مع قيم العنصر <input> باستخدام تركيبة من الأحداث مع سمات v-bind وهذا ما يفعله الموجّه v-model، كما تختلف تركيبة الأحداث والسمات وفقًا لنوع حقل الإدخال وستتطلب شيفرةً برمجيةً أكبر من مجرد استخدام صيغة v-model المختصرة.

لنختبر استخدام الموجِّه v-model عن طريق تسجيل قيمة البيانات المقدَّمة في التابع onSubmit()‎، إذ يمكن الوصول إلى سمات البيانات في المكوّنات باستخدام الكلمة this، وبالتالي يمكن الوصول إلى الحقل label بالصورة this.label، لذا عدّل التابع onSubmit()‎ ليبدو كما يلي:

methods: {
  onSubmit() {
    console.log('Label value: ', this.label);
  }
},

عد الآن إلى تطبيقك المُشغَّل، وأضِف نصًا في الحقل <input> ثم انقر على زر "الإضافة Add"، إذ يجب أن ترى القيمة التي أدخلتها مسجلةً في الطرفية كما يلي على سبيل المثال:

Label value: My value

تغيير سلوك الموجه v-model باستخدام المعدلات

يمكننا إضافة معدِّلات Modifiers لتغيير سلوك الموجِّه v-model بطريقة مماثلة لمعدِّلات الأحداث، ومن أبزر هذه المعدِّلات:

  • ‎.trim: يزيل المسافة الفارغة الموجودة قبل نص الإدخال أو بعده، ويمكننا إضافة هذا المُعدِّل إلى تعليمة v-model بالصورة v-model.trim="label"‎
  • ‎.lazy: يتغير هذا المعدِّل عندما يتزامن v-model مع قيمة نص حقل الإدخال، كما يمكن مزامنة الموجِّه v-model عن طريق تحديث المتغير الذي يستخدم الأحداث، بينما تحدُث هذه المزامنة مع نص حقل الإدخال باستخدام الحدث input، ويعني ذلك أنّ إطار عمل Vue يزامن البيانات بعد كل ضغطة مفتاح، في حين يتسبب المعدِّل ‎.lazy في أن يستخدِم الموجّه v-model الحدث change بدلًا من ذلك، وهذا يعني أنّ Vue لن يزامن البيانات إلّا عندما يفقد حقل الإدخال التركيز أو عند إرسال الاستمارة، وهذا منطقي أكثر لأننا نحتاج إلى البيانات النهائية فقط.

ملاحظة: يمكن استخدام المعدِّلَين ‎.trim و‎.lazy مع بعضهما البعض من خلال استخدامهما بالشكل v-model.lazy.trim="label"‎.

عدّل السمة v-model إلى سلسلة lazy و trim كما هو موضَّح أعلاه، ثم اختبر تطبيقك مرةً أخرى، وجرّب إرسال قيمة بمسافة فارغة في كل نهاية مثلًا.

تمرير البيانات إلى العناصر الآباء مع الأحداث المخصصة

يجب الآن تمرير عنصر المهام الذي أنشأناه إلى المكوِّن App من خلال جعل المكوِّن ToDoForm يصدر حدثًا مخصَّصًا يمرِّر البيانات، وجعل المكون App يستمع إلى هذا الحدث، وتعمل هذه الطريقة بصورة مشابهة جدًا للأحداث الأصيلة مع عناصر HTML، إذ يمكن للمكوِّن الابن إصدار حدث يمكن الاستماع إليه باستخدام الموجِّه v-on.

لنضِف الحدث todo-added إلى الحدث onSubmit الخاص بالمكوِّن ToDoForm، كما يمكن إصدار الأحداث المخصَّصة بالصورة this.$emit("event-name")‎، ويجدر بالذكر أنّ معالجات الأحداث حساسة لحالة الأحرف ولا يمكن أن تتضمن مسافات، وتُحوَّل قوالب Vue إلى أحرف صغيرة، مما يعني أنها لا تستطيع الاستماع إلى الأحداث المُسمَّاة بأحرف كبيرة.

ضَع ما يلي بدلًا من التابع console.log()‎ الموجود في التابع onSubmit()‎:

this.$emit("todo-added");

ارجع بعد ذلك إلى المكوِّن App.vue وأضِف الخاصية methods إلى كائن المكوِّن الذي يحتوي على التابع addToDo()‎ كما هو موضّح أدناه، إذ يمكن لهذا التابع فقط إظهار العبارة To-do added على الطرفية حاليًا.

export default {
  name: 'app',
  components: {
    ToDoItem,
    ToDoForm
  },
 data() {
    return {
      ToDoItems: [
        { id:uniqueId('todo-'), label: 'Learn Vue', done: false },
        { id:uniqueId('todo-'), label: 'Create a Vue project with the CLI', done: true },
        { id:uniqueId('todo-'), label: 'Have fun', done: true },
        { id:uniqueId('todo-'), label: 'Create a to-do list', done: false }
      ]
    };
  },
  methods: {
    addToDo() {
      console.log('To-do added');
    }
  }
};

أضِف بعد ذلك مستمع حدث إلى الحدث todo-added في العنصر <to-do-form></to-do-form>، إذ يستدعي هذا المستمع التابع addToDo()‎ عند إطلاق الحدث، وسيبدو المستمع بالصورة ‎@todo-added="addToDo"‎ باستخدام الاختصار @:

<to-do-form @todo-added="addToDo"></to-do-form>

يجب أن ترى سجل الطرفية من التابع addToDo()‎ عند إرسال المكوِّن ToDoForm، ويُعَدّ ذلك أمرًا جيدًا، لكننا ما زلنا لا نعيد أيّ بيانات إلى المكوِّن App.vue، ويمكن ذلك عن طريق إعادة وسائط إضافية إلى الدالة this.$emit()‎ في المكوِّن ToDoForm، إذ نريد في حالتنا تمرير بيانات العنصر label مع الحدث عند إطلاقه من خلال تضمين البيانات التي نريد تمريرها بوصفها معاملًا آخرًا في التابع ‎$emit()‎ بالصورة this.$emit("todo-added", this.label)‎، وهذا مشابه لكيفية تضمين أحداث جافاسكربت الأصيلة للبيانات باستثناء أنّ أحداث Vue المخصَّصة التي لا تتضمن أيّ كائن حدث افتراضيًا، وبالتالي سيتطابق الحدث المنطلق مع أيّ كائن ترسله مباشرةً، كما سيكون كائن الحدث في حالتنا سلسلةً نصيةً فقط، لذا عدِّل التابع onSubmit()‎ كما يلي:

onSubmit() {
  this.$emit('todo-added', this.label)
}

يمكن التقاط هذه البيانات ضمن المكوِّن App.vue من خلال إضافة معامِل إلى التابع addToDo()‎ الذي يتضمن عنصر label خاص بعنصر المهام الجديد، لذا ارجع إلى المكوِّن App.vue وعدّله ليبدو كما يلي:

methods: {
  addToDo(toDoLabel) {
    console.log('To-do added:', toDoLabel);
  }
}

إذا اختبرت استمارتك مرةً أخرى، فسترى أنّ أيّ نص تدخله مسجَّل في الطرفية عند الإرسال، كما يمرِّر Vue تلقائيًا الوسائط بعد أن يكون اسم الحدث في this.$emit()‎ هو معالِج الأحداث خاصتك.

إضافة المهام الجديدة إلى بياناتنا

يجب الآن إضافة عنصر يمثِّل بيانات المكوِّن ToDoForm التي أصبحت متوفرةً في المكوِّن App.vue في المصفوفة ToDoItems، ويمكن ذلك عن طريق دفع كائن عنصر مهام جديد إلى المصفوفة التي تحتوي على البيانات الجديدة.

عدِّل التابع addToDo()‎ كما يلي:

addToDo(toDoLabel) {
  this.ToDoItems.push({id:uniqueId('todo-'), label: toDoLabel, done: false});
}

اختبر الاستمارة مرةً أخرى، إذ يُفترَض أن ترى عناصر مهام جديدة تُلحَق بنهاية القائمة، وإذا أرسلتَ الاستمارة وحقل الإدخال فارغ، فستُضاف عناصر المهام التي لا تحتوي على نص إلى القائمة، ويمكن إصلاح ذلك من خلال منع تشغيل الحدث todo-added عندما يكون الاسم فارغًا، وبما أنّ الاسم قد أزيل باستخدام الموجِّه ‎.trim، فيجب اختبار السلسلة النصية الفارغة فقط، لذا ارجع إلى المكوِّن ToDoForm وعدِّل التابع onSubmit()‎ كما يلي، وإذا كانت قيمة الحقل label فارغةً، فيجب عدم إصدار الحدث todo-added.

onSubmit() {
  if(this.label === "") {
    return;
  }
  this.$emit('todo-added', this.label);
}

جرّب استمارتك مرةً أخرى، إذ لن تتمكّن الآن من إضافة عناصر فارغة إلى قائمة المهام.

02_rendered-form-with-new-items.png

استخدام الموجه v-model لتحديث قيمة حقل الإدخال

لا يزال العنصر <input> يحتوي على القيمة القديمة بعد الإرسال، ويمكن إصلاح ذلك لأننا نستخدم الموجّه v-model لربط البيانات بالعنصر <input> في المكوِّن ToDoForm، فإذا ضبطنا معامِل الاسم name ليكون سلسلة نصية فارغة، فسيُحدَّث حقل الإدخال.

عدِّل التابع onSubmit()‎ الخاص بالمكوِّن ToDoForm كما يلي:

onSubmit() {
  if(this.label === "") {
    return;
  }
  this.$emit('todo-added', this.label);
  this.label = "";
}

إذا نقرتَ الآن على زر "الإضافة Add"، فسيمسح حقل إدخال المهمة الجديدة "new-todo-input" نفسه.

الخلاصة

يمكننا الآن إضافة عناصر المهام إلى استمارتنا، وبالتالي بدأ تطبيقنا الآن يصبح تفاعليًا، ولكننا تجاهلنا شكله وتنسيقه تمامًا، وسنركز في المقال التالي على إصلاح ذلك، كما سنتعرف على الطرق المختلفة التي يوفرها إطار العمل Vue لمكوّنات التنسيق.

ترجمة -وبتصرّف- للمقال Adding a new todo form: Vue events, methods, and models.

اقرأ أيضًا

#oqpahameedq

تعليقات