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

التوجيه Routing في إطار العمل Ember


Ola Abbas

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

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

الترشيح المستند إلى عنوان URL

يأتي إطار عمل Ember مع نظام التوجيه ذي التكامل الوثيق مع عنوان URL الخاص بالمتصفح، إذ يجب عند كتابة تطبيقات الويب تمثيل الصفحة بعنوان URL بحيث إذا كانت الصفحة بحاجة إلى تحديث، فلن يُفاجَأ المستخدِم بحالة تطبيق الويب، وسيتمكّن من الانتقال مباشرةً إلى عروض التطبيق المهمة.

لدينا حاليًا صفحة "جميع المهام All" إذ لن نطبّق ترشيحًا فيها في الوقت الحالي، لكن يجب إعادة تنظيمها قليلًا للتعامل مع عرض مختلف للمهام النشطة والمهام المكتملة، كما يحتوي تطبيق Ember على مسار تطبيق افتراضي مرتبط بالقالب app/templates/application.hbs الذي يُعَدّ نقطة الدخول إلى تطبيق قائمة المهام، لذلك يجب إجراء بعض التغييرات للسماح بالتوجيه Routing.

إنشاء المسارات

لننشئ ثلاثة مسارات جديدة هي: "Index" و "Active" و "Completed" من خلال إدخال الأوامر التالية في الطرفية ضمن المجلد الجذر لتطبيقك:

ember generate route index
ember generate route completed
ember generate route active

لا ينتج عن الأمرَين الثاني والثالث ملفات جديدةً فحسب، وإنما يُعدَّل الملف app/router.js الموجود مسبقًا الذي يحتوي على المحتويات التالية:

import EmberRouter from '@ember/routing/router';
import config from './config/environment';

export default class Router extends EmberRouter {
  location = config.locationType;
  rootURL = config.rootURL;
}

Router.map(function() {
  this.route('completed');
  this.route('active');
});

يتصرّف الملف router.js بوصفه خريطة موقع sitemap للمطورين ليتمكنوا من رؤية كيفية تنظيم التطبيق بأكمله بسرعة، كما أنه يخبر إطار العمل Ember بكيفية التفاعل مع مسارك عند تحميل بيانات عشوائية أو التعامل مع الأخطاء أثناء تحميل تلك البيانات أو تفسير الأجزاء الآلية لعنوان URL مثلًا، وبما أنّ بياناتنا ساكنة، فلن نصل إلى أيّ من هذه الميزات الرائعة، لكننا سنظل نتأكد من أنّ المسار يوفِّر الحد الأدنى من البيانات المطلوبة لعرض الصفحة.

لم يضِف إنشاء المسار "Index" سطر تعريف مسار إلى الملف router.js، لأن "Index" هي كلمة خاصة تشير إلى المسار الافتراضي للتصيير والتحميل وغير ذلك كما هو الحال مع التنقل باستخدام عنوان URL وتحميل وحدة جافاسكربت JavaScript.

يمكنك تعديل طريقتنا القديمة في تصيير تطبيق قائمة المهام من خلال استبدال الاستدعاء {{outlet}} باستدعاء المكون TodoList من قالب التطبيق ب، مما يعني تصيير أيّ مسار فرعي في المكان نفسه دون الانتقال إلى تبويب جديد.

انتقل إلى الملف todomvc/app/templates/application.hbs وضَع مكان السطر التالي:

<TodoList />

ما يلي:

{{outlet}}

يمكننا الآن إدخال استدعاء المكوّن TodoList في قوالب index.hbs و completed.hbs و active.hbs الموجودة أيضًا في مجلد القوالب.

ضَع مكان السطر التالي:

{{outlet}}

ما يلي:

<TodoList />

إذا جرّبت التطبيق مرةً أخرى وزرتَ أيًّا من المسارات الثلاثة:

localhost:4200 
localhost:4200/active 
localhost:4200/completed

فسترى الشيء نفسه بالضبط، إذ سيصيَّر القالب الذي يتوافق مع المسار المحدد ("Active" أو "Completed" أو "Index") في كل عنوان URL للمكون <TodoList /‎>.

يُحدَّد الموقع في الصفحة حيث يُصيَّر المكوّن <TodoList /‎> باستخدام {{ outlet }} في المسار الأب وهو في هذه الحالة application.hbs، ولدينا الآن مساراتنا في مكانها الصحيح، لكننا بحاجة إلى طريقة للتمييز بين كل من هذه المسارات لتظهِر ما يفترض منها أن تظهِره.

ارجع مرةً أخرى إلى الملف todo-data.js الذي يحتوي مسبقًا على تابع جالب getter يعيد جميع المهام وتابع جالب آخر يعيد المهام غير المكتملة، ولكنه لا يحتوي على تابع جالب يعيد المهام المكتملة فقط، لذا لنضفه بعد التوابع الجالبة الموجودة مسبقًا:

get completed() {
  return this.todos.filter(todo => todo.isCompleted);
}

النماذج

يجب الآن إضافة نماذج Models إلى مسارات ملفات جافاسكربت للسماح بسهولة بإعادة مجموعات بيانات معينة لعرضها في تلك النماذج، إذ يُعَدّ النموذج model بأنه خطاف Hook دورة حياة تحميل بيانات، ولكن ليست قدرات النموذج مهمةً لنا في تطبيق TodoMVC، كما سنوفر الوصول إلى الخدمة كما فعلنا مع المكوّنات.

نموذج المسار index

عدِّل أولًا الملف todomvc/app/routes/index.js ليبدو كما يلي:

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class IndexRoute extends Route {
  @service('todo-data') todos;

  model() {
    let todos = this.todos;

    return {
      get allTodos() {
        return todos.all;
      }
    }
  }
}

يمكننا الآن تعديل الملف todomvc/app/templates/index.hbs بحيث إذا تضمّن المكوّن <TodoList /‎>، فإنه يفعل ذلك صراحة مع النموذج المتاح، ويستدعي التابع الجالب allTodos()‎ للتأكد من ظهور جميع المهام.

عدِّل السطر التالي في هذا الملف:

<TodoList />

إلى ما يلي:

<TodoList @todos={{ @model.allTodos }}/>

نموذج المسار completed

عدِّل الملف todomvc/app/routes/completed.js ليبدو كما يلي:

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class CompletedRoute extends Route {
  @service('todo-data') todos;

  model() {
    let todos = this.todos;

    return {
      get completedTodos() {
        return todos.completed;
      }
    }
  }
}

يمكننا الآن تعديل الملف todomvc/app/templates/completed.hbs بحيث إذا تضمّن المكوّن ‎<TodoList /‎>‎، فإنه يفعل ذلك صراحةً مع النموذج المتاح، ويستدعي التابع الجالب completedTodos()‎ للتأكد من ظهور المهام المكتملة فقط.

عدِّل السطر التالي في هذا الملف:

<TodoList />

إلى ما يلي:

<TodoList @todos={{ @model.completedTodos }}/>

نموذج المسار active

عدِّل الملف todomvc/app/routes/active.js ليبدو كما يلي:

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class ActiveRoute extends Route {
  @service('todo-data') todos;

  model() {
    let todos = this.todos;

    return {
      get activeTodos() {
        return todos.incomplete;
      }
    }
  }
}

يمكننا الآن تعديل الملف todomvc/app/templates/active.hbs بحيث إذا تضمّن المكوّن <TodoList /‎>، فإنه يفعل ذلك صراحةً مع النموذج المتاح، ويستدعي التابع الجالب activeTodos()‎ للتأكد من ظهور المهام النشطة أو غير المكتملة فقط.

عدِّل السطر التالي في هذا الملف:

<TodoList />

إلى ما يلي:

<TodoList @todos={{ @model.activeTodos }}/>

لاحظ أننا نعيد كائنًا له تابع جالب بدلًا من كائن ساكن أو قائمة مهام ساكنة مثل this.todos.completed في خطافات نماذج المسار، والسبب في ذلك هو أننا نريد أن يكون للقالب مرجع آلي لقائمة المهام، فإذا أعدنا القائمة مباشرةً، فلن يُعاد حساب البيانات أبدًا، مما يؤدي إلى ظهور تنقلات فاشلة وعدم ترشيح فعلي، ويمكن إعادة البحث عن المهام من خلال تحديد تابع جالب في كائن الإعادة من بيانات النموذج بحيث تُمثَّل التعديلات التي أجريناها على قائمة المهام في القائمة المُصيَّرة.

تشغيل روابط التذييل

أصبحت وظائف المسار الآن في مكانها الصحيح، لكن لا يمكننا الوصول إليها من تطبيقنا، لذا لنفعِّل روابط التذييل بحيث يؤدي النقر عليها إلى الانتقال إلى المسارات المطلوبة.

ارجع إلى الملف todomvc/app/components/footer.hbs وابحث عمّا يلي:

<a href="#">All</a>
<a href="#">Active</a>
<a href="#">Completed</a>

وعدِّله إلى ما يلي:

<LinkTo @route='index'>All</LinkTo>
<LinkTo @route='active'>Active</LinkTo>
<LinkTo @route='completed'>Completed</LinkTo>

يُعَدّ <LinkTo> بأنه مكوّن Ember مبني مسبقًا يعالِج جميع تغييرات الحالة عند التنقل بين المسارات، ويضبط الصنف active على أيّ رابط يطابق عنوان URL في حالة وجود رغبة في تنسيقه تنسيقًا مختلفًا عن الروابط غير النشطة.

تحديث عرض المهام ضمن قائمة المهام

أحد الأشياء الصغيرة الأخيرة التي نحتاج إلى إصلاحها هو أننا كنا ندخل سابقًا إلى خدمة todo-data مباشرةً ونكرّر جميع المهام ضمن الملف todomvc/app/components/todo-list.hbs كما يلي:

{{#each this.todos.all as |todo| }}

بما أننا نريد أن يعرض مكوّن قائمة المهام قائمةً مُرشَّحةً، فيجب تمرير وسيط إلى مكوِّن قائمة المهام يمثِّل قائمة المهام الحالية من todos كما يلي:

{{#each @todos as |todo| }}

يجب أن يحتوي تطبيقك الآن على روابط فعّالة في التذييل تعرض مسارات "Index" أو المسارات الافتراضية و"النشطة Active" و"المكتملة Completed".

01_todos-navigation.gif

هناك الكثير مما يجب تنفيذه قبل أن يتطابق ما نفّذناه حتى الآن مع تطبيق TodoMVC الأصلي مثل تعديل المهام وحذفها واستمرارها عبر عمليات إعادة تحميل الصفحة، كما يمكنك مشاهدة تطبيق Ember المكتمل من خلال التحقق من مجلد التطبيق النهائي في مستودع شيفرة تطبيقنا للحصول عليها أو شاهد الإصدار المباشر.

ادرس الشيفرة لمعرفة المزيد حول إطار عمل Ember، وراجع الفقرة التالية التي توفِّر مزيدًا من الموارد وبعض نصائح استكشاف الأخطاء وإصلاحها.

استكشاف الأخطاء العامة وإصلاحها وبنى gotcha والمفاهيم الخاطئة

كان آخر تحديث للقائمة التالية في شهر 6 من عام 2020.

كيف يمكنني تنقيح الأخطاء التي يظهرها إطار العمل؟

توجد الإضافة ember-inspector بالنسبة للأشياء الخاصة بإطار العمل والتي تسمح بفحص ما يلي:

  • المسارات Routes والمتحكِّمات Controllers.
  • المكوِنات.
  • الخدمات.
  • الوعود Promises.
  • البيانات وهي بيانات من واجهة برمجة تطبيقات بعيدة مثل ember-data افتراضيًا.
  • معلومات الإهمال Deprecation Information.
  • تصيير الأداء.

اطّلع على أدوات المطوِّر التي تفيدك في تنقيح الأخطاء، وتعرّف على كيفية استخدام أدوات المطوِّر DevTools في كروم Chrome.

بوجد ملفان رئيسيان من ملفات جافاسكربت هما: vendor.js و ‎{app-name}.js في أيِّ مشروع Ember افتراضي، ويُنشَآن هذان الملفان باستخدام خرائط الشيفرة البرمجية Sourcemaps، لذلك ستُحمَّل خريطة الشيفرة البرمجية وستُوضَع نقطة التوقف في شيفرة مُترجَمة مسبقًا لتسهيل الارتباط بشيفرة مشروعك باستخدام منقّح أخطاء عند فتح الملف vendor.js أو الملف ‎{app-name}.js للبحث عن الشيفرة البرمجية ذات الصلة.

هل سأحتاج إلى واجهة ember-data المثبتة مسبقا؟

لن تحتاجها على الإطلاق، إذ تحل واجهة ember-data المشاكل الأكثر شيوعًا التي سيعمل بها أيّ تطبيق يتعامل مع البيانات بحيث يمكن تشغيل عميل بيانات واجهتك الأمامية، وهناك بديل شائع لأيّ عميل بيانات واجهة أمامية كامل الميزات هو Fetch API، إذ سيبدو المسار Route باستخدام fetch()‎ كما يلي باستخدام أنماط التصميم التي يوفرها إطار العمل:

import Route from '@ember/routing/route';

export default class MyRoute extends Route {
  async model() {
    let response = await fetch('some/url/to/json/data');
    let json = await response.json();

    return {
      data: json
    };
  }
}

لماذا لا يمكنني استخدام جافاسكربت فقط؟

هذا هو السؤال الأكثر شيوعًا الذي يسمعه مجتمع Ember من الأشخاص الذين لديهم خبرة سابقة في React، إذ يمكن استخدام صيغة JSX أو أيّ شكل آخر من أشكال إنشاء نموذج DOM، ولكن ليس هناك شيء قوي مثل نظام قوالب Ember، حيث يفرض الحد الأدنى منه قرارات معينة، ويسمح بشيفرة برمجية أكثر تناقسًا مع الحفاظ على القالب أكثر هيكلية بدلًا من ملئه بشيفرة برمجية حسب الرغبة.

ما هي حالة المساعد mut؟

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

<Checkbox
  @value={{this.someData}}
  @onToggle={{fn (mut this.someData) (not this.someData)}}
/>

بينما ستكون هناك حاجة إلى صنف مكوّن بدون استخدام المساعدmut:

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class Example extends Component {
  @tracked someData = false;

  @action
  setData(newValue) {
    this.someData = newValue;
  }
}

سيُستدعَى هذا الصنف بعد ذلك في القالب كما يلي:

<Checkbox @data={{this.someData}} @onChange={{this.setData}} />

يمكن أن يكون استخدام mut مرغوبًا نظرًا لاختصاره، ولكنه يحتوي على دلالات غريبة يمكن أن تتسبّب في الكثير من الارتباك لمعرفة المقصود منها.

كانت هناك بعض الأفكار الجديدة التي وُضِعت معًا في شكل إضافات تستخدِم واجهات برمجة تطبيقات ember-set-helper و ember-box، ويحاول كلاهما حل مشاكل mut من خلال تقديم مفاهيم أوضح، وتجنب تحولات وقت البناء وسلوك آلة Glimmer الافتراضية الضمني.

باستخدام ember-set-helper:

<Checkbox
  @value={{this.someData}}
  @onToggle={{set this "someData" (not this.someData)}}
/>

باستخدام ember-box:

{{#let (box this.someData) as |someData|}}
  <Checkbox
    @value={{unwrap someData}}
    @onToggle={{update someData (not this.someData)}}
  />
{{/let}}

لا تُعَدّ أيّ من هذه الحلول شائعةً بين أعضاء المجتمع، ولا يزال الأشخاص يحاولون اكتشاف واجهة برمجة تطبيقات مريحة وبسيطة لضبط البيانات في سياق القالب فقط دون أصناف جافاسكربت الداعمة.

ما هو الغرض من المتحكمات؟

المتحكِّمات Controllers هي أنماط مفردة Singletons يمكن أن تساعد في إدارة تصيير السياق الخاص بالمسار النشط، وتعمل إلى حد كبير مثل أصناف جافاسكربت الداعمة للمكوّن، والمتحكِّمات هي -من شهر 1 عام 2020- الطريقة الوحيدة لإدارة محدّدات استعلام عناوين URL، إذ يجب أن تكون المتحكِّمات خفيفةً إلى حد ما في مسؤولياتها وفي تفويض المكوّنات والخدمات حيثما أمكن ذلك.

ما هو الغرض من المسارات؟

يمثِّل المسار Route جزءًا من عنوان URL عندما ينتقل المستخدِم من مكان إلى آخر في التطبيق، فالمسار له المسؤوليات التالية فقط:

  • تحميل الحد الأدنى من البيانات المطلوبة لتصيير المسار أو عرض الشجرة الفرعية.
  • يُعَدّ بوابة الوصول إلى المسار وإعادة التوجيه إذا لزم الأمر.
  • التعامل مع حالات التحميل والخطأ في الحد الأدنى من البيانات المطلوبة.

يحتوي المسار على ثلاثة خطّافات لدورة الحياة فقط وجميعها اختيارية وهي:

  • beforeModel: بوابة الوصول إلى المسار.
  • model: مكان تحميل البيانات.
  • afterModel: للتحقق من الوصول.

يتمتع المسار بالقدرة على التعامل مع الأحداث الشائعة الناتجة عن إعداد الخطاف model:

  • loading: ما يجب تطبيقه عند تحميل الخطّاف model.
  • error: ما يجب فعله عند حدوث خطأ في الخطّاف model.

يمكن لكل من loading و error تصيير القوالب الافتراضية بالإضافة إلى القوالب المخصَّصة المحددة في مكان آخر في التطبيق، مما يوحِّد حالات التحميل أو حالات الخطأ، كما يمكن العثور على مزيد من المعلومات حول ما يمكن للمسار تنفيذه في توثيق واجهة برمجة التطبيقات API.

أخيرًا، إذا احتجت إلى أي مساعدة، فأضف سؤالك ضمن قسم الأسئلة والأجوبة في أكاديمية حسوب أو ضمن مجتمع البرمجة في حسوب IO وستصل إلى إجابتك من مجتمع المطورين العرب.

ترجمة -وبتصرّف- للمقالين Routing in Ember وEmber resources and troubleshooting.

اقرأ أيضًا


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

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

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



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

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

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

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   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.


×
×
  • أضف...