اذهب إلى المحتوى

تنفيذ التفاعل في تطبيق Ember: وظيفة التذييل والعرض الشرطي


Ola Abbas

حان الوقت الآن لمعالجة وظيفة التذييل Footer في تطبيقنا، إذ سنحدِّث عدّاد المهام لإظهار العدد الصحيح للمهام التي يجب إكمالها، وسنطبّق التنسيق بصورة صحيحة على المهام المكتملة من خلال تحديد مربع الاختيار، كما سنفعِّل زر "مسح المهام المكتملة Clear completed"، وسنتعرّف على استخدام التصيير أو العرض الشرطي Conditional Rendering في قوالبنا (التصيير والعرض والإخراج هي مترادفات لكلمة rendering).

  • المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript، ومعرفة استخدام سطر الأوامر أو الطرفية، إذ يُعَدّ فهم ميزات جافاسكربت الحديثة مثل الأصناف Classes والوحدات Modules وما إلى ذلك مفيدًا للغاية، لأن إطار العمل Ember يستخدمها بكثرة.
  • الهدف: مواصلة تعلّم أصناف المكوّنات من خلال التعرّف على التصيير الشرطي وتفعيل بعض وظائف التذييل.

توصيل السلوك بالتذييل

يجب تطبيق الوظائف الثلاث التالية لكي يعمل التذييل:

  • عدّاد المهام المعلَّقة.
  • مرشحات لجميع المهام والمهام النشطة والمهام المكتملة.
  • زر لمسح المهام المكتملة.

أولًا، يجب إنشاء صنف Class للتذييل بما أننا نحتاج إلى الوصول إلى الخدمة من مكوّن التذييل، لذلك أدخِل الأمر التالي في الطرفية:

ember generate component-class footer

ثانيًا، ابحث بعد ذلك عن الملف todomvc/app/components/footer.js وعدّله إلى ما يلي:

import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class FooterComponent extends Component {
  @service('todo-data') todos;
}

ثالثًا، يجب الآن العودة إلى الملف todo-data.js وإضافة بعض الوظائف التي ستسمح بإعادة عدد المهام غير المكتملة لمعرفة عدد المهام المتبقية، ووظيفة مسح المهام المكتملة من القائمة التي يحتاجها زر "مسح المهام المكتملة Clear completed"، لذلك أضِف في الملف todo-data.js التابع الجالب Getter التالي بعد الجالب all()‎ الموجود سابقًا لتحديد المهام غير المكتملة:

get incomplete() {
  return this.todos.filterBy('isCompleted', false);
}

يمكننا باستخدام التابع Array.Proxy.filterBy()‎ في Ember ترشيح الكائنات في المصفوفة بسهولة بناءً على شروط مساواةٍ بسيطة، إذ نريد في جزء الشيفرة السابق الحصول على جميع عناصر المهام عندما تكون الخاصية isCompleted مساوية للقيمة false، وسيُعاد حساب هذا التابع الجالب عندما تتغير قيمة الكائن في المصفوفة لأن الخاصية isCompleted مُميَّزة بالمزخرِف ‎@tracked في الكائن Todo.

رابعًا، أضف بعد ذلك ما يلي بعد الدالة add(text)‎ الموجودة مسبقًا:

@action
clearCompleted() {
  this.todos = this.incomplete;
}

يُعَدّ ذلك رائعًا لمسح المهام، إذ نحتاج فقط إلى ضبط المصفوفة todos لتساوي قائمة المهام غير المكتملة.

خامسًا، أخيرًا، يجب الاستفادة من هذه الوظيفة الجديدة في قالب footer.hbs، لذلك انتقل إلى هذا الملف الآن.

سادسًا، ضَع أولًا مكان السطر التالي:

<strong>0</strong> todos left

ما يلي، إذ يُملَأ عدد المهام غير المكتملة بطول المصفوفة incomplete:

<strong>{{this.todos.incomplete.length}}</strong> todos left

سابعًا، ثم ضع مكان السطر التالي:

<button type="button" class="clear-completed">

ما يلي:

<button type="button" class="clear-completed" {{on 'click' this.todos.clearCompleted}}>

إذا نقرتَ على الزر الآن، فسيُشغَّل الإجراء clearCompleted()‎ الذي أضفناه سابقًا، ولكن إذا حاولت النقر على زر "مسح المهام المكتملة Clear Completed"، فلن يبدو التطبيق أنه يفعل أيّ شيء بسبب عدم وجود طريقة لإكمال المهام حاليًا، كما يجب توصيل القالب todo.hbs بالخدمة، بحيث يؤدي تحديد مربع الاختيار المتعلق به إلى تغيير حالة كل مهمة.

مشكلة كتابة todos بدلا من todo

لدينا مشكلة صغيرة أخرى نتعامل معها، إذ تشير العبارة "todos left" إلى وجود عدد من المهام المتبقية بالرغم من وجود مهمة واحدة متبقية أحيانًا، وهذا سيء قواعديًا.

يمكن حل هذه المشكلة من خلال تعديل هذا الجزء من القالب ليحتوي على التصيير الشرطي، إذ يمكنك في Ember تصيير أجزاء من القالب شرطيًا باستخدام المحتوى الشرطي مثل الكتلة البسيطة التالية:

{{#if this.thingIsTrue}}
  Content for the block form of "if"
{{/if}}

ضع مكان الجزء التالي من footer.hbs:

<strong>{{this.todos.incomplete.length}}</strong> todos left

ما يلي:

<strong>{{this.todos.incomplete.length}}</strong>
  {{#if this.todos.incomplete.length === 1}}
    todo
  {{else}}
    todos
  {{/if}}
    left

سيؤدي ذلك إلى إعطاء خطأ، ولكن لا تستطيع عبارات if البسيطة هذه في Ember حاليًا اختبار تعبير معقد مثل الموازنة، وإنما تستطيع اختبار قيمة الصواب أو الخطأ فقط، لذلك يجب إضافة جالب getter إلى الملف todo-data.js لإعادة النتيجة this.incomplete.length === 1 ثم استدعاؤها في القالب.

أضف الجالب الجديد الآتي إلى الملف todo-data.js بعد التوابع الجالبة الموجودة مسبقًا مباشرةً، ولاحظ أننا نحتاج إلى this.incomplete.length وليس this.todos.incomplete.length لأننا نطبّق ذلك ضمن الخدمة حيث يتوفر الجالب incomplete()‎ مباشرةً، كما أنّ محتويات الخدمة متوفرة في القالب مثل المهام todos عبر التعليمة ‎@service('todo-data') todos;‎ ضمن صنف التذييل، وبالتالي سيكون this.todos.incomplete.length هناك.

get todoCountIsOne() {
  return this.incomplete.length === 1;
}

ارجع بعد ذلك إلى footer.hbs وعدِّل قسم القالب السابق الذي عدّلناه إلى ما يلي:

<strong>{{this.todos.incomplete.length}}</strong>
  {{#if this.todos.todoCountIsOne}}
    todo
  {{else}}
    todos
  {{/if}}
    left

احفظ الملف واختبره، وسترى الكلمة الصحيحة المُستخدَمة عندما يكون لديك عنصر واحد لتنفيذه.

لاحظ صيغة كتلة if في Ember، ويمكنك استخدام الشكل المضمَّن التالي:

{{if this.todos.todoCountIsOne "todo" "todos"}}

استكمال المهام

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

إنشاء الصنف todo

أولًا، شغّل الأمر التالي في الطرفية:

ember generate component-class todo

ثانيًا، انتقل الآن إلى الملف todomvc/app/components/todo.js وعدّل محتوياته لتبدو كما يلي لمنح المكوّن todo إمكانية الوصول إلى الخدمة:

import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class TodoComponent extends Component {
  @service('todo-data') todos;
}

ثالثًا، ارجع مرةً أخرى إلى ملف الخدمة todo-data.js وأضف الإجراء التالي بعد الإجراءات السابقة مباشرةً، مما سيسمح بتبديل حالة الاكتمال لكل مهمة:

@action
toggleCompletion(todo) {
  todo.isCompleted = !todo.isCompleted;
}

تحديث القالب لإظهار الحالة المكتملة

أخيرًا، سنعدّل القالب todo.hbs بحيث ترتبط قيمة مربع الاختيار بالخاصية isCompleted في المهمة، حيث يُستدعَى التابع toggleCompletion()‎ في خدمة المهمة عند التعديل.

أولًا، ابحث أولًا عن السطر التالي في الملف todo.hbs:

<li>

وضَع مكانه ما يلي، إذ ستلاحظ أننا نستخدم المحتوى الشرطي لإضافة قيمة الصنف إذا كان ذلك مناسبًا:

<li class="{{ if @todo.isCompleted 'completed' }}">

ثانيًا، ابحث بعد ذلك عمّا يلي:

<input
  aria-label="Toggle the completion of this todo"
  class="toggle"
  type="checkbox"
>

وضَع مكانه ما يلي:

<input
  class="toggle"
  type="checkbox"
  aria-label="Toggle the completion of this todo"
  checked={{ @todo.isCompleted }}
  {{ on 'change' (fn this.todos.toggleCompletion @todo) }}
>

ملاحظة: يستخدِم جزء الشيفرة السابق كلمةً مفتاحيةً جديدةً خاصةً بإطار عمل Ember هي fn التي تسمح بالتطبيق الجزئي Partial Application، وهو مشابه للتابع bind لكنه لا يغير سياق الاستدعاء، ويكافئ استخدام التابع bind مع الوسيط الأول null.

أعِد تشغيل خادم التطوير وانتقل إلى المضيف المحلي localhost:4200 مرةً أخرى، وسترى أنه لدينا عدّاد "المهام المتبقية todos left" وزر "المسح Clear":

01_todos-being-marked-completed-and-cleared.gif

يمكن أن تسأل نفسك لماذا لا نطبّق التبديل على المكوِّن فقط؟ نظرًا لأن الدالة قائمة بذاتها ولا تحتاج على الإطلاق إلى أيّ شيء من الخدمة، وبما أننا في النهاية سنرغب في الاستمرار أو مزامنة جميع تغييرات قائمة المهام مع التخزين المحلي (اطّلع على الإصدار الأخير من التطبيق)، فستكون جميع عمليات تغيير الحالة المستمرة في المكان نفسه.

الخلاصة

يمكننا الآن وضع علامة على المهام المكتملة ومسحها أيضًا، والشيء الوحيد المتبقي لتفعيل التذييل هو عمليات ترشيح "جميع المهام All" و"المهام النشطة Active" و"المهام المكتملة Completed"، إذ سنطبّق ذلك في المقال التالي باستخدام التوجيه Routing.

ترجمة -وبتصرّف- للمقال Ember Interactivity: Footer functionality, conditional rendering.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...