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

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

  • المتطلبات الأساسية: يوصَى على الأقل بأن تكون على دراية بأساسيات لغات HTML وCSS وجافاسكربت JavaScript، ومعرفة باستخدام سطر الأوامر أو الطرفية، وستحتاج طرفية مثبَّت عليها node وnpm لتصريف وبناء تطبيقك.
  • الهدف: تعلّم وتطبيق بعض مفاهيم Svelte الأساسية مثل إنشاء المكونات وتمرير البيانات باستخدام الخاصيات وتصيير Render تعابير جافاسكربت في شيفرة HTML وتعديل حالة المكونات وتكرارها عبر القوائم.

يمكن متابعة كتابة شيفرتك معنا، لذلك انسخ أولًا مستودع github -إذا لم تفعل ذلك مسبقًا- باستخدام الأمر التالي:

git clone https://github.com/opensas/mdn-svelte-tutorial.git

ثم يمكنك الوصول إلى حالة التطبيق الحالية من خلال تشغيل الأمر التالي:

cd mdn-svelte-tutorial/03-adding-dynamic-behavior

أو يمكنك تنزيل محتوى المجلد مباشرةً كما يلي:

npx degit opensas/mdn-svelte-tutorial/03-adding-dynamic-behavior

تذكَّر تشغيل الأمر npm install && npm run dev لبدء تشغيل تطبيقك في وضع التطوير، وإذا أردت متابعتنا فابدأ بكتابة الشيفرة باستخدام الأداة REPL من هنا.

التعامل مع المهام

يعرِض المكوِّن Todos.svelte حاليًا شيفرة HTML ثابتةً لا تتغير، إذًا لنبدأ في جعله أكثر ديناميكيةً، إذ سنأخذ معلومات المهام من شيفرة HTML ونخزنها في المصفوفة todos، كما سننشئ متغيرين لتتبّع العدد الإجمالي للمهام والمهام المكتملة، إذ ستُمثَّل حالة المكوِّن من خلال هذه المتغيرات الثلاثة ذات المستوى الأعلى.

أولًا، أنشئ قسم <script> قبل المكوِّن src/components/Todos.svelte وضَع فيه المحتوى التالي:

<script>
  let todos = [
    { id: 1, name: "Create a Svelte starter app", completed: true },
    { id: 2, name: "Create your first component", completed: true },
    { id: 3, name: "Complete the rest of the tutorial", completed: false }
  ];

  let totalTodos = todos.length;
  let completedTodos = todos.filter((todo) => todo.completed).length;
</script>

لنبدأ بعد ذلك بإظهار رسالة الحالة، لذا ابحث عن العنوان <h2> الذي له المعرِّف id بالقيمة list-heading واستبدل العدد الثابت للمهام النشطة والمكتملة بتعابير ديناميكية كما يلي:

<h2 id="list-heading">{completedTodos} out of {totalTodos} items completed</h2>

انتقل إلى التطبيق وسترى الرسالة "‎2 out of 3 items completed" كما كانت سابقًا، ولكن تأتي المعلومات هذه المرة من المصفوفة todos.

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

إنشاء مهام من بيانات يدخلها المستخدم

تُعَدّ عناصر المهام المعروضة ثابتةً حاليًا، ونريد تكرار كل عنصر في المصفوفة todos وتصيير Render شيفرة HTML لكل مهمة. ليس لدى لغة HTML طريقة للتعبير عن المنطق مثل التعابير الشرطية والحلقات، ولكن إطار عمل Svelte يمكنه ذلك من خلال استخدام الموجّه {‎#each...‎} للتكرار عبر المصفوفة todos.

يتضمن المعامِل الثاني -إذا كان موجودًا- فهرس العنصر الحالي، كما يمكن توفير تعبير مفتاحي يحدد كل عنصر بطريقة فريدة، وسيستخدِمه إطار Svelte لمعرفة الاختلاف في القائمة عند تغيير البيانات بدلًا من إضافة العناصر أو إزالتها في النهاية، ويُعَدّ تحديد عنصر مفتاحي دائمًا من الممارسات الجيدة، كما يمكن توفير كتلة ‎:else التي ستُصيَّر عندما تكون القائمة فارغةً.

أولًا، استبدل العنصر <ul> الحالي بالإصدار المبسط التالي لتفهم كيفية العمل:

<ul>
{#each todos as todo, index (todo.id)}
  <li>
    <input type="checkbox" checked={todo.completed}/> {index}. {todo.name} (id: {todo.id})
  </li>
{:else}
  Nothing to do here!
{/each}
</ul>

ثانيًا، ارجع إلى التطبيق وسترى شيئًا يشبه ما يلي:

إنشاء مهام من بيانات يدخلها المستخدم

رأينا الآن أنّّ كل شيء يعمل جيدًا، فلننشئ عنصر مهمة مكتملة مع كل حلقة للموجّه {‎#each}، ونضمّن فيها المعلومات من المصفوفة todos مثل id وname وcompleted واستبدل كتلة <ul> الحالية بما يلي:

<!-- Todos -->
<ul role="list" class="todo-list stack-large" aria-labelledby="list-heading">
  {#each todos as todo (todo.id)}
    <li class="todo">
      <div class="stack-small">
        <div class="c-cb">
          <input type="checkbox" id="todo-{todo.id}" checked={todo.completed}/>
          <label for="todo-{todo.id}" class="todo-label">
            {todo.name}
          </label>
        </div>
        <div class="btn-group">
          <button type="button" class="btn">
            Edit <span class="visually-hidden">{todo.name}</span>
          </button>
          <button type="button" class="btn btn__danger">
            Delete <span class="visually-hidden">{todo.name}</span>
          </button>
        </div>
      </div>
    </li>
  {:else}
    <li>Nothing to do here!</li>
  {/each}
</ul>

لاحظ كيفية استخدام الأقواس المعقوصة لتضمين تعابير جافاسكربت ضمن سمات HTML كما فعلنا مع السمتين checked و id لمربع الاختيار.

حوّلنا بذلك شيفرة HTML الثابتة إلى قالب ديناميكي جاهز لعرض المهام من حالة المكوِّن.

التعامل مع الخاصيات

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

أولًا، استبدل كتلة let todos = ...‎ الموجودة مسبقًا في Todos.svelte بالتعليمة التالية:

export let todos = []

يمكن أن يبدو هذا غريبًا بعض الشيء في البداية، فهذه ليست الطريقة التي تعمل بها تعليمة export في وحدات جافاسكربت، وإنما هي الطريقة التي يوسّع بها إطار عمل Svelte شيفرة جافاسكربت من خلال استخدام صيغة صالحة لهدف جديد.

يستخدِم إطار عمل Svelte في حالتنا الكلمة export لتمييز التصريح عن متغير بوصفه خاصية Property أو Prop، مما يعني أنه يصبح في متناول مستخدِمي المكوِّن، كما يمكنك تحديد قيمة أولية افتراضية للخاصية التي تُستخدَم إذا لم يحدد مستخدِم المكون الخاصية الخاصة بالمكوِّن أو إذا كانت قيمتها الأولية غير محدَّدة عند إنشاء نسخة من المكوِّن، لذا نخبر إطار Svelte من خلال التعليمة export let todos = []‎ أنّ المكوِّن Todos.svelte سيقبل السمة todos والتي ستُهيَّأ إلى مصفوفة فارغة عند حذفها.

ثانيًا، ألقِ نظرةً على التطبيق وسترى الرسالة "Nothing to do here!‎" لأننا لا نمرّر حاليًا أيّ قيمة إليه من المكوِّن App.svelte، لذلك سيستخدِم القيمة الافتراضية.

ثالثًا، لننقل مصفوفة مهامنا todos إلى المكوِّن App.svelte ولنمرّرها إلى المكوِّن Todos.svelte بوصفها خاصيةً، لذا عدِّل المكوِّن src/App.svelte كما يلي:

<script>
  import Todos from "./components/Todos.svelte";

  let todos = [
    { id: 1, name: "Create a Svelte starter app", completed: true },
    { id: 2, name: "Create your first component", completed: true },
    { id: 3, name: "Complete the rest of the tutorial", completed: false }
  ];
</script>

<Todos todos={todos} />

أخيرًا، يسمح إطار عمل Svelte بتحديد المتغير بوصفه اختصارًا عندما يكون للسمة والمتغير الاسم نفسه، ويمكننا بذلك إعادة كتابة السطر الأخير كما يلي:

<Todos {todos} />

يجب أن تُصيَّر المهام كما كانت سابقًا باستثناء أننا نمرّرها الآن من المكوِّن App.svelte.

تبديل وإزالة المهام

سنضيف الآن بعض الوظائف لتبديل حالة المهمة، إذ يحتوي إطار Svelte على الموجّه on:eventname للاستماع إلى أحداث DOM، لذا أضِف معالجًا إلى الحدث on:click الخاص بمربع الاختيار لتبديل القيمة المكتملة.

أولًا، عدّل العنصر <input type="checkbox"‎> في المكوِّن src/components/Todos.svelte كما يلي:

<input type="checkbox" id="todo-{todo.id}"
  on:click={() => todo.completed = !todo.completed}
  checked={todo.completed}
/>

ثانيًا، سنضيف دالة لإزالة مهمة من المصفوفة todos، لذا أضف الدالة removeTodo()‎ في الجزء السفلي من القسم <script> في المكوِّن Todos.svelte كما يلي:

function removeTodo(todo) {
  todos = todos.filter((t) => t.id !== todo.id)
}

سنستدعي بعد ذلك هذه الدالة باستخدام زر "الحذف Delete"، لذا عدّلها باستخدام الحدث click كما يلي:

<button type="button" class="btn btn__danger"
  on:click={() => removeTodo(todo)}
>
  Delete <span class="visually-hidden">{todo.name}</span>
</button>

يُعَدّ تمرير نتيجة تنفيذ دالة بوصفها معالجًا بدلًا من تمرير الدالة من الأخطاء الشائعة جدًا في المعالجات في إطار Svelte، فإذا حدّدت on:click={removeTodo(todo)}‎ مثلًا، فستُنفَّذ الدالة removeTodo(todo)‎ وستُمرَّر النتيجة بوصفها معالِجًا، لذا يجب تحديد on:click={() => removeTodo(todo)}‎ على أساس معالج، فإذا لم تأخذ الدالة removeTodo()‎ أيّ معامِل، فيمكنك استخدام on:event={removeTodo}‎، ولكن لا يمكنك استخدام on:event={removeTodo()}‎، كما أنّ ليس هذا الشكل صيغةً خاصةً من Svelte، وإنما استخدمنا دوال جافاسكربت السهمية Arrow Functions العادية.

يمكننا الآن حذف المهام، إذ تُزال المهام ذات الصلة من المصفوفة todos عند الضغط على زر حذف عنصر المهمة، وتُحدَّث واجهة المستخدِم لعدم إظهاره لاحقًا، كما يمكننا الآن تحديد مربعات الاختيار، وستُحدَّث الحالة المكتملة للمهام ذات الصلة في المصفوفة todos، لكن لا يُحدَّث العنوان "x out of y items completed".

المهام التفاعلية

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

لا ينطبق الأمر نفسه على المتغيرين totalTodos وcompletedTodos، إذ يُسنَد إليهما قيمة عند إنشاء نسخة من المكوِّن وينفَّذ السكربت في الشيفرة التالية، ولكن لا تُعدَّل قيمتهما بعد ذلك:

let totalTodos = todos.length
let completedTodos = todos.filter((todo) => todo.completed).length

يمكننا إعادة حسابهما بعد تبديل المهام وإزالتها، ولكن هناك طريقة أسهل لذلك، حيث نخبر إطار Svelte بأننا نريد أن يكون المتغيران totalTodos و completedTodos تفاعليين من خلال جعلهما مسبوقين بالرمز :$‎، إذ سينشئ إطار Svelte الشيفرة لتحديثهما تلقائيًا كلما تغيرت البيانات التي يعتمدان عليها.

ملاحظة: يستخدِم إطار Svelte صيغة تعليمة تسمية جافاسكربت :$‎ لتمييز التعليمات التفاعلية مثل الكلمة export المستخدَمة للتصريح عن الخاصيات، إذ يُعَد هذا المثال مثالًا آخرًا يستفيد فيه إطار Svelte من صيغة جافاسكربت صالحة مع إعطائها هدفًا جديدًا، وهو في هذه الحالة "إعادة تشغيل هذه الشيفرة كلما تغيرت أيّ من القيم المشار إليها".

عدِّل تعريف المتغيرين totalTodos وcompletedTodos ضمن الملف src/components/Todos.svelte لتبدو كما يلي:

$: totalTodos = todos.length
$: completedTodos = todos.filter((todo) => todo.completed).length

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

يحلّل مصرِّف Svelte الشيفرة لإنشاء شجرة اعتماديات، ثم ينشئ شيفرة جافاسكربت لإعادة تقييم كل تعليمة تفاعلية كلما حُدِّثت إحدى اعتمادياتها، كما تُطبَّق التفاعلية في Svelte بطريقة خفيفة الوزن وفعالة دون استخدام المستمعِين Listeners أو التوابع الجالبة Getters أو الضابطة Setters أو أيّ آلية معقدة أخرى.

إضافة مهام جديدة

يجب الآن إضافة بعض الوظائف لإضافة مهام جديدة.

أولًا، سننشئ متغيرًا للاحتفاظ بنص المهام الجديدة، لذا أضف التصريح التالي إلى القسم <script> في الملف Todos.svelte:

let newTodoName = ''

سنستخدِم الآن هذه القيمة في العنصر <input> لإضافة مهام جديدة، وسنحتاج ربط المتغير newTodoName بدخل todo-0، بحيث تبقى قيمة المتغير newTodoName متزامنةً مع الخاصية value الخاصة بالعنصر <input> كما يلي:

<input value={newTodoName} on:keydown={(e) => newTodoName = e.target.value} />

كلما تغيرت قيمة المتغير newTodoName، فسينتقل هذا التغيير إلى السمة value الخاصة بحقل الإدخال، وكلما ضُغِط على مفتاح في حقل الإدخال، فسنحدّث محتويات المتغير newTodoName، إذ يُعَدّ ذلك تطبيقًا يدويًا لربط البيانات ثنائي الاتجاه لحقل الإدخال، لكننا لسنا بحاجة لهذه الآلية، إذ يوفِّر إطار Svelte طريقةً أسهل لربط أيّ خاصية بمتغير باستخدام الموجّه bind:property كما يلي:

<input bind:value={newTodoName} />

إذًا لنعدّل حقل الإدخال todo-0 كما يلي:

<input
  bind:value={newTodoName}
  type="text"
  id="todo-0"
  autocomplete="off"
  class="input input__lg" />

يمكن اختبار نجاح هذه الطريقة من خلال إضافة تعليمة تفاعلية لتسجيل محتويات المتغير newTodoName، لذا أضف مقتطف الشيفرة التالي في نهاية القسم <script>:

$: console.log('newTodoName: ', newTodoName)

ملاحظة: لا تقتصر التعليمات التفاعلية على التصريح عن المتغيرات، إذ يمكنك وضع أيّ تعليمة جافاسكربت بعد الرمز :$‎.

ارجع الآن إلى المضيف المحلي localhost:5042 واضغط على الاختصار Ctrl + Shift + K لفتح طرفية المتصفح واكتب شيئًا ما في حقل الإدخال، ويجب أن ترى إدخالاتك مسجلةً، كما يمكنك الآن حذف التابع console.log()‎ التفاعلي إذا رغبت في ذلك.

سننشئ بعد ذلك دالةً لإضافة مهمة جديدة وهي الدالة addTodo()‎ التي ستدفع كائن todo جديد إلى المصفوفة todos، لذا أضف ما يلي إلى الجزء السفلي من كتلة <script> ضمن الملف src/components/Todos.svelte:

function addTodo() {
  todos.push({ id: 999, name: newTodoName, completed: false })
  newTodoName = ''
}

ملاحظة: سنسنِد حاليًا المعرِّف id نفسه لكل مهمة، ولكن لا تقلق إذ سنصلح ذلك لاحقًا.

نريد الآن تحديث ملف HTML لاستدعاء الدالة addTodo()‎ كلما أُرسِل النموذج، لذا عدّل وسم فتح النموذج NewTodo كما يلي:

<form on:submit|preventDefault={addTodo}>

يدعم الموجّه on:eventname إضافة مُعدِّلات إلى حدث DOM باستخدام المحرف |، حيث يخبر المُعدِّل preventDefault إطار Svelte بإنشاء شيفرة لاستدعاء التابع event.preventDefault()‎ قبل تشغيل المعالج.

إذا حاولت إضافة مهام جديدة، فستُضاف هذه المهام الجديدة إلى المصفوفة todos، ولكن لن تُحدَّث واجهة المستخدِم، وتذكَّر أنه في إطار Svelte يبدأ التفاعل باستخدام الإسنادات، وهذا يعني تنفيذ الدالة addTodo()‎ وإضافة عنصر إلى المصفوفة todos، ولكن لن يكتشف إطار Svelte أن تابع الدفع قد عدّل المصفوفة، وبذلك لن يحدّث مهام العنصر <ul>، كما ستؤدي إضافة todos = todos إلى نهاية الدالة addTodo()‎ إلى حل هذه المشكلة، ولكن يبدو تضمين ذلك في نهاية الدالة أمرًا غريبًا، لذلك سنأخذ التابع push()‎ مع استخدام صيغة الانتشار Spread Syntax لتحقيق النتيجة نفسها، إذ سنسند قيمة إلى المصفوفة todos تساوي المصفوفة todos بالإضافة إلى الكائن الجديد.

ملاحظة: المصفوفة Array لديها العديد من العمليات المتغيرة مثل push()‎ و pop()‎ و splice()‎ و shift()‎ و unshift()‎ و reverse()‎ و sort()‎ التي يمكن أن يتسبب استخدامها في حدوث آثار جانبية وأخطاء يصعب تتبعها، لذا نتجنب تغيّر المصفوفة باستخدام صيغة الانتشار بدلًا من التابع push()‎، ويُعَدّ ذلك من الممارسات جيدة.

عدّل الدالة addTodo()‎ كما يلي:

function addTodo() {
  todos = [...todos, { id: 999, name: newTodoName, completed: false }]
  newTodoName = ''
}

إعطاء كل مهمة معرفا فريدا

إذا حاولت إضافة مهام جديدة في تطبيقك الآن، فستتمكن من إضافة مهام جديدة وستظهر في واجهة المستخدِم أيضًا، ولكن إذا جربته مرةً ثانية، فلن يعمل، وستتلقى رسالة تقول "Error: Cannot have duplicate keys in a keyed each"، أي نحتاج إلى معرِّفات فريدة لمهامنا.

لنصرّح أولًا عن المتغير newTodoId يُحسَب من عدد المهام زائد 1، ولنجعله تفاعليًا، لذا أضِف مقتطف الشيفرة التالي إلى القسم <script>:

let newTodoId
  $: {
    if (totalTodos === 0) {
      newTodoId = 1;
    } else {
      newTodoId = Math.max(...todos.map((t) => t.id)) + 1;
    }
  }

ملاحظة: لا تقتصر التعليمات التفاعلية على سطر واحد One-liners، كما يمكن استخدام التعليمة التفاعلية الآتية:

 $: newTodoId = totalTodos ? Math.max(...todos.map(t => t.id)) + 1 : 1

والتي تُعَدّ مفيدةً أيضًا، ولكنه أقل قابليةً للقراءة.

يحلّل المصرِّف التعليمة التفاعلية بأكملها، ويكتشف أنها تعتمد على المتغير totalTodos والمصفوفة todos. لذا كلما عُدِّل أيّ منهما، فسيُعاد تقييم هذه الشيفرة وتحديث newTodoId وفقًا لذلك، ولنستخدِم ذلك في الدالة addTodo()‎، ولنعدّلها كما يلي:

function addTodo() {
  todos = [...todos, { id: newTodoId, name: newTodoName, completed: false }]
  newTodoName = ''
}

ترشيح المهام حسب الحالة

لنطبّق الآن القدرة على ترشيح مهامنا حسب الحالة، لذا سننشئ متغيرًا للاحتفاظ بالمرشِّح الحالي، ودالة مساعدة ستعيد المهام المُرشَّحة.

أولًا، أضف ما يلي في الجزء السفلي من القسم <script>:

let filter = 'all'
  const filterTodos = (filter, todos) =>
    filter === 'active' ? todos.filter((t) => !t.completed) :
    filter === 'completed' ? todos.filter((t) => t.completed) :
    todos

نستخدِم المتغير filter للتحكم في مرشّح جميع المهام all أو المهام النشطة active أو المكتملة completed، إذ سيؤدي إسناد إحدى هذه القيم إلى المتغير filter إلى تفعيل المرشح وتحديث قائمة المهام، إذ ستتلقى الدالة filterTodos()‎ المرشِّح الحالي وقائمة المهام وستعيد مصفوفةً جديدةً من المهام المُرشَّحة وفقًا لذلك.

لنحدّث بعد ذلك شيفرة HTML الخاصة بزر الترشيح لجعله ديناميكيًا ولنحدّث المرشِّح الحالي عندما يضغط المستخدِّم على أحد أزرار الترشيح كما يلي:

<div class="filters btn-group stack-exception">
  <button class="btn toggle-btn" class:btn__primary={filter === 'all'} aria-pressed={filter === 'all'} on:click={()=> filter = 'all'} >
    <span class="visually-hidden">Show</span>
    <span>All</span>
    <span class="visually-hidden">tasks</span>
  </button>
  <button class="btn toggle-btn" class:btn__primary={filter === 'active'} aria-pressed={filter === 'active'} on:click={()=> filter = 'active'} >
    <span class="visually-hidden">Show</span>
    <span>Active</span>
    <span class="visually-hidden">tasks</span>
  </button>
  <button class="btn toggle-btn" class:btn__primary={filter === 'completed'} aria-pressed={filter === 'completed'} on:click={()=> filter = 'completed'} >
    <span class="visually-hidden">Show</span>
    <span>Completed</span>
    <span class="visually-hidden">tasks</span>
  </button>
</div>

سنعرِض المرشِّح الحالي من خلال تطبيق الصنف btn__primary على زر ترشيح المهام النشطة، ويمكنك تطبيق أصناف تنسيق CSS شرطيًا على عنصر من خلال استخدام الموجِّه class:name={value}‎، فإذا قُيِّمت عبارة القيمة على أنها صحيحة، فسيُطبَّق اسم الصنف، كما يمكنك إضافة العديد من هذه الموجّهات بشروط مختلفة إلى العنصر نفسه، لذلك إذا كانت التعليمة class:btn__primary={filter === 'all'}‎، فسيطبّق إطار Svelte الصنف btn__primary إذا كان المرشح يساوي جميع المهام all.

ملاحظة: يوفِّر إطار العمل Svelte اختصارًا يتيح لنا إمكانية اختصار <div class:active={active}‎> إلى <div class:active> عندما يتطابق الصنف Class مع اسم المتغير.

يحدث شيء مشابه مع aria-pressed={filter === 'all'}‎ عند تقييم تعبير جافاسكربت الممرَّر بين الأقواس المعقوصة إلى قيمة صحيحة، حيث ستُضاف السمة aria-pressed إلى الزر، وبالتالي سنحدِّث متغير filter باستخدام class:btn__primary={filter === 'all'}‎ كلما نقرنا على الزر.

يجب الآن استخدام الدالة المساعدة في حلقة {‎#each} كما يلي:

...
  <ul role="list" class="todo-list stack-large" aria-labelledby="list-heading">
  {#each filterTodos(filter, todos) as todo (todo.id)}
...

يكتشف إطار Svelte بعد تحليل شيفرتنا أنّ الدالة filterTodos()‎ تعتمد على المتغيرين filter و todos، وكلما تغيرت أيّ من هذه الاعتماديات، فسيُحدَّث نموذج DOM وفقًا لذلك، لذا كلما تغيَّر المتغيران filter وtodos، فسيُعاد تقييم الدالة filterTodos()‎ وستُحدَّث العناصر الموجودة ضمن الحلقة.

ملاحظة: يمكن أن تكون التفاعلية خادعةً في بعض الأحيان، إذ يتعرّف إطار Svelte على المتغير filter بوصفه اعتماديةً لأننا نشير إليه في التعبيرfilterTodos(filter, todo)‎، إذ يُعَدّ المتغير filter متغيرًا من المستوى الأعلى، لذلك يمكن إزالته من معامِلات الدالة المساعدة واستدعائه بالشكل: filterTodos(todo)‎، كما يمكن أن ينجح هذا الأمر، ولكن ليس لدى إطار Svelte الآن طريقةً لمعرفة أنّ ‎{‎#each filterTodos(todos)... }‎ يعتمد على المتغير filter، ولن تُحدَّث قائمة المهام المُرشَّحة عندما يتغير المرشّح، وتذكَّر دائمًا أنّ إطار Svelte يحلِّل الشيفرة لاكتشاف الاعتماديات، لذلك يُفضَّل أن تكون صريحًا بشأنه وألّا تعتمد على رؤية متغيرات المستوى الأعلى، كما يُعَدّ جعل الشيفرة واضحةً وصريحةً بشأن المعلومات التي تستخدِمها من الممارسات الجيدة.

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

cd mdn-svelte-tutorial/04-componentizing-our-app

أو يمكنك تنزيل محتوى المجلد مباشرةً باستخدام الأمر التالي:

npx degit opensas/mdn-svelte-tutorial/04-componentizing-our-app

تذكَّر تشغيل الأمر npm install && npm run dev لبدء تشغيل تطبيقك في وضع التطوير، فإذا أردت متابعتنا، فابدأ بكتابة الشيفرة باستخدام الأداة REPL.

الخلاصة

طبّقنا في هذا المقال معظم الوظائف المطلوبة، إذ يمكن لتطبيقنا عرض وإضافة وحذف المهام وتبديل حالتها المكتملة وإظهار عدد المهام المكتملة وتطبيق المرشحات، حيث غطينا المواضيع التالية:

  • إنشاء واستخدام المكونات.
  • تحويل شيفرة HTML الثابتة إلى قالب حي.
  • تضمين تعابير جافاسكربت في شيفرة HTML.
  • التكرار على القوائم باستخدام الموجّه {‎#each}.
  • تمرير المعلومات بين المكونات باستخدام الخاصيات.
  • الاستماع إلى أحداث DOM.
  • التصريح عن التعليمات التفاعلية.
  • تنقيح الأخطاء الأساسي باستخدام التابع console.log()‎ والتعليمات التفاعلية.
  • ربط خاصيات HTML بالموجّه bind:property.
  • بدء التفاعل باستخدام الإسنادات.
  • استخدام العبارات التفاعلية لترشيح البيانات.
  • التعريف الصريح عن الاعتماديات التفاعلية.

سنضيف مزيدًا من الوظائف التي ستسمح للمستخدِمين بتعديل المهام في المقال التالي.

ترجمة -وبتصرُّف- للمقال Dynamic behavior in Svelte: working with variables and props.

اقرأ أيضًا


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

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

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



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

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

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

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


×
×
  • أضف...