الكائنات والمصفوفات (Objects & Arrays)
الدرس 6 من 21 · دورة TanStack Form الكاملة · 13 دقيقة و29 ثانية
الفيديو الأصلي باللغة الإنجليزية. إذا تعذّر تشغيله هنا، شاهده على موقع المؤلف. التفريغ النصي العربي الكامل أدناه.
مستودع شيفرة هذا الدرس
الفرع:
objects-arrays-booleans
[00:02] حتى نضيف مدخلات (inputs) من أجل تغيير حقولنا. غير أنّنا جرّبنا أن نغيّر اسمَينا الأول والأخير. لكن ماذا عن تغيير الحقول المتشعّبة (nested)؟ إذًا إذا أردنا تغيير street داخل address، فكيف سنفعل ذلك
[00:15] في TanStack Form؟ والحقيقة أنّه أمر بسيط جدًا. ففلننزل إلى هنا في نموذجنا. لنقل أنّه بعد الاسم الأخير لدينا، وهو داخل subscribe هنا، فلنضع حقلًا (field) جديدًا، وكل ما يلزمنا فعله ببساطة هو أنّه عندما نحدّد الاسم إزاء
[00:30] حقلنا — حقل، عذرًا، form.Field هنا — فحين نضع الاسم لدينا نضع الكائن المتشعّب (nested object) أو العنصر المتشعّب داخل الكائن باستخدام النقاط (dots)، ونحن — بل نحصل فعلًا على إكمالات (autocomplete) هنا كي تتحصّل على فكرة عن الكيفية. فإذا
[00:46] أردنا تغيير street مثلًا، نكتب address.street ثم داخل هذا تكون الأمور تمامًا مشابهة لما فعلناه سابقًا. فأصبح لدينا دالة نتلقّى فيها حقلنا، ويمكننا في هنا أن نعيد الحقل لدينا. سأقوم
[01:00] بلصق هذا الترميز (markup)، وسيكون تمامًا مشابهًا لما فعلناه سابقًا.
[01:06] إذًا الآن لدينا حقل street المدخل، وإذا غيّرناه فسيتغيّر street المتشعّب داخل address.
[01:14] لكن ماذا عن المصفوفات (arrays)؟ ففي هنا لدينا مصفوفة skills هذه، ولنفترض أنّه يلزمنا أن نتمكّن من إضافة عناصر إلى هذه المصفوفة، وإزالة عناصر، وتغيير أشياء داخل عناصر المصفوفة. تذكّر أنّ هذه مصفوفة مهارات (skills) و
[01:29] ستضمّ كل مهارة name وID ومستوى (level). ولنفترض أنّك تريد تغيير مستوى مهارةٍ ما داخل مصفوفتنا. كيف يمكننا معالجة ذلك في TanStack Form؟ أولًا سأضع بعض المهارات الوهمية (dummy skills) مبدئيًا في
[01:43] القيَم الافتراضية (default values). فسأضع في هنا هكذا مصفوفة من المهارات الوهمية: مجرد name، ومعرّف (ID) عشوائي، ومستوى. وكل ما يلزمنا فعله أولًا هو عرض مهاراتنا فقط. ففي هنا، فلنذهب بعد الحقل الذي أضفناه للتوّ، وهو
[01:59] حقل street. ولنضع حقلًا جديدًا.
[02:02] إذًا form.Field. وهذا سيتعلّق بمهاراتنا. غير أنّه يلزمنا فعل شيء آخر هنا. فبالإضافة إلى الاسم — عذرًا — skills — يلزمنا أيضًا إضافة mode بقيمة array، صحيح؟
[02:16] والآن داخل هذا سنحصل كالعادة على دالة مع حقلنا هكذا، ويمكننا في هنا أن نعيد ترميزنا (markup). في ترميزنا، فلنغلّف كل شيء بـ div ربما، ولنضيف نصًّا: skills. آه،
[02:31] عذرًا. ثم ربما داخل div آخر، لنجري حلقة (loop) تمرّ على مهاراتنا الحالية. سنجد ذلك في field.state.value، وستكون هذه مصفوفة يمكننا المرور عليها عبر map، ولكل عنصر — لنسمّه
[02:48] skill. وستتوافر لدينا أيضًا قيمة الفهرس (index). ثم يمكننا أن نعيد شيئًا لكل مهارة. ففي هنا، فلنشئ أيضًا div ربما، وداخله فلنعرض اسم مهارتنا، ولنضيف شرطةً (dash) هنا ونعرض
[03:07] مستوى المهارة كذلك.
[03:11] إذا حفظنا هذا، فسنحصل على — فلنجري تجديدًا (refresh) ربما.
[03:17] وفلنخفِ ربما هذه، أو ربما لنضع هذا الكائن الذي نسجّل طباعته (logging) أسفل نموذجنا.
[03:26] سيتعين أن نلقي نظرة عليه. لكن فلنضعه هنا في الأسفل ربما. إذًا الآن ينبغي أن نحصل على مهاراتنا، ولدينا قائمة المهارات هنا: HTML، وCSS، وJavaScript وما إلى ذلك. وما يلزمنا فعله بعد ذلك هو أن
[03:41] نتمكّن من إضافة مهارات جديدة، وإزالة مهارات، وتغيير مستوى المهارة كذلك أيضًا. سأجعل الأمور أوضح قليلًا بسرعة.
[03:50] فلنمنح زر الإرسال (submit) هذا بعض التنسيقات (styles). وكذلك فلنضيف لهذه الوسمة pre بعض المسافة هنا. وعذرًا، الهامش العلوي (margin top).
[04:01] وسأبقي عليه — سيتعين أن نلقي نظرة عليه لاحقًا. لكن الآن فلنتدبّر إزالة المهارات وإضافة المهارات كذلك. وقد جعل TanStack Form هذا بسيطًا للغاية فعلًا. TanStack Form
[04:14] سيتيح لنا بعض الدوال (functions) التي ستجعل الإضافة والإزالة سهلَيْن جدًا. ففي هنا، لكل مهارة، لنفترض أنّه يلزمنا إضافة زر إزالة (remove button). وبالطبع فإني في هنا لا أمنح
[04:25] اهتمامًا كبيرًا لترميزنا وتنسيقاته وما إلى ذلك؛ يكفينا التركيز على المفاهيم فقط. فلنضيف زرًّا سريعًا هنا. عذرًا، زرًّا هكذا. ولنضع ربما X هنا، وسأمنحه بعض التنسيقات. إذًا
[04:40] سيكون لدينا هذا الزر. والآن فلنفعل شيئًا عند النقر (on click). فعند النقر يلزمنا إزالة المهارة.
[04:49] فكيف يمكننا ذلك؟ في TanStack Form يصبح لدينا، داخل حقلنا، دالة هنا لإزالة العناصر من هذه المصفوفة. فـ field.removeValue ثم نمرّر في هنا فهرس (index) القيمة التي نريد إزالتها. وبالطبع
[05:03] نحصل على الفهرس هنا من دالة map لدينا، وهذا تقريبًا كل شيء. فإذا نقرنا الآن على هذا فسنزيل مهاراتنا.
[05:11] وبالطبع كما ترى ينعكس هذا هنا كذلك. فأصبحت لدينا الآن مصفوفة فارغة. فلنجدّد. ولنضيف زرًّا آخر لإضافة عناصر إلى هذه المصفوفة، وهو بسيط جدًا أيضًا.
[05:22] إذًا، فلنذهب في هنا بعد حلقتنا، ربما بعد div هذا، ولنضيف زرًّا لإضافة مهارة (skill) جديدة. سأضيف زرًّا. فلنضيف بعض النصّ: Add skill.
[05:36] في هنا سأضيف بعض التنسيق (styling).
[05:38] إذًا لدينا هذا الزر هنا. ثم عند النقر فلنضف مهارة جديدة. وبالطبع سأضيف هنا مهارة وهمية فقط؛ يمكن هذا في الواقع أن يكون شيئًا متكاملًا (integrated) مع واجهة خلفية (back end) تمتلكها وأمور مثل
[05:50] هذا. لكن سأضيف مهارةً وهمية. فليُضاف شيئًا إلى مصفوفتنا نستدعي field.pushValue ونمرّر قيمة (value) جديدة ببساطة. وفي حالتنا لدينا مصفوفة من الكائنات (objects)، فيلزمنا إضافة كائن هنا يضمّ
[06:04] name. وللاسم سأضيف مهارة جديدة فقط. ويمكننا ربما، في هنا، فحص الطول الحالي لحقلنا. فهو field.state.value.length ثم نضيف إليه واحدًا ربما — نصّ عشوائي فقط — ثم سأضيف أيضًا
[06:24] معرّفًا عشوائيًّا حيث يمكننا استخدام crypto.randomUUID، وأخيرًا سأضيف مستوى، فلنقل beginner، وبمجرّد ذلك الآن، إذا نقرنا على Add skill فسنحصل على مهارة
[06:39] جديدة مضافة هنا، وبالطبع ومن دون ذكر البديهي فإنّه إن تأملنا قيمة حقل المهارات لدينا فسنمتلك العناصر الجديدة في مصفوفتنا كذلك.
[06:51] حسنًا. فلنفعل الآن آخر شيء أخير، وهو تعديل مستوى المهارة داخل عناصرنا داخل مصفوفتنا.
[07:00] فيلزمنا امتلاك حقول لمعالجة هذه القيمة، القيمة التي داخل مصفوفتنا بالنسبة إلى المستوى داخل هذا الكائن، صحيح؟ فكيف يمكننا فعل ذلك؟ فلنعد إلى هنا.
[07:14] فلننظّف الأمور قليلًا ربما. فلنفتح div الذي يضمّ مهاراتنا. وبالمناسبة، يلزمنا في حلقتنا إضافة key هنا، فلنقل skill.id. وما سأفعله أنّي سأزيل ربما
[07:29] المستوى من هنا؛ فسنعرضه بطريقة مختلفة. ولنغلّف اسم المهارة في span أيضًا.
[07:36] ولنجعل كل شيء متجاورًا بإضافة اسم صنف (class name) كـ flex وitems-center هكذا، ولنضيف هامشًا سفليًّا (margin bottom) ولنضيف فراغًا (gap) بقيمة اثنين كذلك أيضًا.
[07:51] أصبح لدينا هذا، ويلزمنا الآن وضع قائمة منسدلة (dropdown) بجوار المهارة تعرض مستوى المهارة المختار حاليًّا، وستكون لدينا القدرة على تغييره من هذه القائمة المنسدلة كذلك أيضًا. صحيح؟ فإذًا
[08:03] لاحظ أنّنا كنّا أصلًا داخل حقل هنا وهو حقل skills ويلزمنا وضع حقل آخر داخله الخاص بمستوى المهارة. فعلينا امتلاك نوعٍ من الحقول الفرعية (sub-fields) داخل حقل skills ويمكننا القيام
[08:16] بذلك كالعادة ببساطة بإضافة حقل آخر داخل حقل skills، وهو — تذكّر — ذو mode بقيمة مصفوفة. فلنضعه بعد الاسم هنا. ولنرسم مكانًا إضافيًّا. فلنضع حقلًا آخر لمستوى المهارة. إذًا فلنربط
[08:31] في هنا form.Field وسيكون الاسم — فلنُغلق هذا — سيكون الاسم: داخل مصفوفة skills يلزمنا استهداف مهارة محددة، أي أحد هذه الكائنات، ثم داخل أحدها يلزمنا
[08:47] الوصول إلى level. فسيتعين علينا امتلاك شيء ديناميكي (dynamic) هنا.
[08:52] فليُوضَع هذا في قالب نصّي (template string). فداخل skills يلزمنا استهداف العنصر الحالي في مصفوفتنا، إذ يتوافر لدينا قيمة الفهرس (index). فما يلزمنا بلوغه في نهاية المطاف هو شيء مثل: skills.0
[09:07] level وما إلى ذلك. لكن بالطبع هذا الصفر لا يلزمنا تثبيته (hardcode)، فنحتاجه ليوافق قيمة الفهرس هذه.
[09:15] فليُزَل هذا ونضع قيمة الفهرس لدينا هكذا. فلنعرض، داخل هذا الحقل، قيمتنا بسرعة ربما. سنحصل في هنا، مثل أيّ حقل، على دالة مع حقلنا. فلنسمّها ربما subField
[09:28] هذه المرة، ويمكننا أن نعيد شيئًا ما.
[09:31] فلنعرض للآن subField.state.value فقط ربما. وإذا تأملت الآن فستجد أنّ لدينا قيمتنا: beginner، وbeginner، وbeginner، وهكذا. لكن — بما أنّ
[09:46] لدينا هذا الآن في صورة حقل، صرنا قادرين بالطبع، بدلًا من مجرد عرض هذه القيمة، على إضافة input — قائمة منسدلة — كي نتمكّن من معالجة هذه القيمة كذلك. فسيكون هذا مشابهًا أيضًا لما فعلناه مع input، لكننا
[09:59] سنستخدم قائمة منسدلة بدلًا منه فقط. فلنفعلها هذه المرة: فلنضيف input من نوع select وسأمنحه بعض أسماء الأصناف (class names) للتنسيق فقط.
[10:10] فسينبغي أن يبدو هكذا. ويلزمنا الآن وضع خياراتنا الممكنة وهي beginner، وintermediate، وexpert وما إلى ذلك. ويمكننا فعل ذلك هكذا: نضع بعض وسوم option هنا بقيمنا ونصوصنا. فالآن
[10:27] أصبح لدينا ذلك. وأخيرًا يلزمنا، بشكلٍ مشابهٍ لما فعلناه مع المدخلات الأخرى — أن نعرض قيمتنا بالطبع، ثم نمرّر onChange وonBlur كي إذا غيّرنا حقلنا فإنّ
[10:39] ذلك سيتغيّر في نموذجنا فعلًا. فسيكون هذا مشابهًا جدًا كذلك. فلنفعلها في select لدينا هنا: فلنضع قيمتنا (value) وهي — subField هذه المرة — ثم state.value. وبشكل مشابه، يمكننا أن نفعل blur
[10:59] الذي سيدخل في subField.handleBlur. وأخيرًا onChange — عذرًا — سيحتوي في هنا على مناسبتنا (event) المتضمّنة قيمتنا، وكلما يلزمنا فعله أن نستدعي subField و
[11:17] ثم handleOnChange أو handleChange وتمرّر له القيمة التي ستكون في event.target.value هكذا. وبقيامنا بهذا، فأظنّ أنّه ينبغي أن يكون ذلك كل شيء. الآن إذا غيّرنا
[11:34] شيئًا هنا — فلنغيّر هذا إلى expert — فسينبغي أن يحدّث ذلك نموذجنا وينبغي أن نراه هنا: فنحصل على level بقيمة expert، وبالطبع يمكننا فعل ذلك مع أيّ حقل آخر. ها نحن إذًا. وأصبحنا الآن قادرين على الإضافة والإزالة، وأيضًا التغيير
[11:51] لشيءٍ داخل مصفوفة الكائنات في نموذجنا. فبإمكاننا هكذا التعامل مع المصفوفات والحقول الفرعية في TanStack Form. فقد رأينا كيف نعالج سلاسل نصّية (strings) عادية، ورأينا الكائنات والمصفوفات كذلك. ولنشاهد أخيرًا ومرة أخرى
[12:06] هذه القيمة المنطقية (boolean) التي لدينا هنا وكيف نعالج هذه القيمة كذلك، وهو ما سيكون بسيطًا للغاية فعلًا. فلنفعلها بسرعة.
[12:14] إذًا بعد — ربما — هذا الطويل، أي هذه المصفوفة، فلنضع حقلًا. سألصقه لأنّه سيكون بسيطًا ومباشرًا، مشابهًا جدًا لما أنجزناه سابقًا. فلألصق إذًا هذا الحقل الذي ستكون مسؤوليته عن
[12:28] عنصر acceptTerms المتوافر لدينا هنا وهو قيمة منطقية (boolean).
[12:33] غير أنّه الآن، عند تصيير (render) حقلنا سأصيّر مربّع اختيار (checkbox)، فليديّ هنا input بنوع checkbox وأمرّر ببساطة القيمة وonBlur وonChange.
[12:43] وربما يكون الاختلاف الوحيد هنا أنّ onChange في هنا ستكون قيمتها في event.target.checked وليس event.target.value.
[12:53] وهذا ربما هو الاختلاف الوحيد. وإذا حفظنا هذا فلنمتلك مربّع اختيار (checkbox) هنا؛ فإذا وضعنا عليه علامة (check) فسينبغي أن يحدّث acceptTerms لدينا ويضعه على true، وإن أزلنا العلامة فستكون false. والآن إذًا، وبالطبع، أنا أعلم
[13:08] بما قد يكون في ذهنك: فنحن نمتلك كثيرًا من الشيفرة هنا، الكثير من الشيفرة النموذجية المتكرّرة (boilerplate)، لنموذج بسيط نسبيًّا. غير أنّك لا تقلق، كما ذكرنا سابقًا، ففي نهاية الفيديو سوف
[13:20] نكتشف طريقةً ما لتجريد (abstract) نموذجنا وجعله أصغر حجمًا بكثير.
[13:24] غير أنّه، من أجل الآن، فلنركّز على موضوعنا المهم التالي، وهو التحقّق من الصحة (validation). فلنشاهد الآن كيف يمكننا