كتابة TodoMVC على dap. الجزء 2

هذا هو الجزء الثاني والأخير من البرنامج التعليمي الذي نكتب فيه عميل TodoMVC باستخدام إطار عمل ds js البسيط التفاعلي.

ملخص الجزء الأول : تلقينا قائمة مهام من الخادم بتنسيق JSON ، وقمنا بإنشاء قائمة HTML منه ، وأضفنا القدرة على تعديل الاسم وعلامة الإنجاز لكل حالة ، وقمنا بتنفيذ إخطار الخادم حول هذه التعديلات.

يبقى أن تتحقق: حذف الحالات التعسفية ، إضافة حالات جديدة ، التثبيت الشامل / إعادة التعيين وتصفية الحالات بناءً على الإكمال ووظيفة حذف جميع الحالات المكتملة. هذا هو ما سنفعله. يمكن الاطلاع على النسخة النهائية من العميل ، والتي سنأتي إليها في هذه المقالة ، هنا .



الخيار الذي استقرناه في المرة الأخيرة يمكن تجديده هنا .

هنا هو رمزه:

'#todoapp'.d("" ,'#header'.d("" ,'H1'.d("") ,'INPUT#new-todo placeholder="What needs to be done?" autofocus'.d("") ) ,'#main'.d("" ,'#toggle-all type=checkbox'.d("") ,'UL#todo-list'.d("*@ todos:query" ,'LI'.d("$completed=.completed $editing= $patch=; a!" ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=($completed=#.checked)") ,'LABEL.view' .d("? $editing:!; ! .title") .e("dblclick","$editing=`yes") ,'INPUT.edit' .d("? $editing; !! .title@value") .ui("$patch=(.title=#.value)") .e("blur","$editing=") ,'BUTTON.destroy'.d("") ) .a("!? $completed $editing") .u("? $patch; (@method`PATCH .url:dehttp headers $patch@):query $patch=") ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) ) .DICT({ todos : "//todo-backend-express.herokuapp.com/", headers: {"Content-type":"application/json"} }) .FUNC({ convert:{ dehttp: url=>url.replace(/^https?\:/,'') } }) .RENDER() 


يوجد الآن خمسون سطرًا فقط ، ولكن بحلول نهاية المقالة سيكون هناك ضعف العدد - ما يصل إلى 100. سيكون هناك العديد من طلبات HTTP إلى الخادم ، لذا يرجى فتح أدوات المطور (في Chrome ، كما تتذكر ، Ctrl + Shift + I) - سيكون هناك أولاً وقبل كل شيء ، تعد علامة تبويب الشبكة مثيرة للاهتمام ، وثانياً ، وحدة التحكم. أيضًا ، لا تنس عرض الكود لكل إصدار من صفحتنا - في Chrome ، يكون Ctrl + U.

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



سيكون هذا الجزء الثاني من البرنامج التعليمي أكثر تعقيدًا وإثارة للاهتمام من الجزء الأول. [TODO: اسأل الرمز المميز للعثور على تلميذ ينفجر صورة الدماغ على شبكة الإنترنت] .

بعد إذنكم ، سأواصل ترقيم الفصول بالجزء 1. هناك عدنا إلى 7. لذلك ،

8. عمل قائمة مهام لمتغير الحالة


لإزالة حالة من القائمة ، يوجد زر BUTTON.destroy . تتمثل الإزالة في إرسال طلب DELETE إلى الخادم وإزالة عنصر UL#todo-list > LI المطابق المقابل UL#todo-list > LI مع كافة المحتويات. مع إرسال طلب DELETE ، كل شيء واضح:

  ,'BUTTON.destroy'.ui("(@method`DELETE .url:dehttp):query") 


ولكن مع إزالة عنصر من الشاشة ، تكون الخيارات ممكنة. يمكن للمرء ببساطة تقديم متغير حالة آخر ، $deleted ، وإخفاء العنصر باستخدام CSS ، بما في ذلك فئة CSS deleted

  ,'LI'.d("$completed=.completed $editing= $patch= $deleted=; a!" //  $deleted   "" ... ,'BUTTON.destroy'.d("(@method`DELETE .url:dehttp):query $deleted=`yes") //  $deleted -     ) .a("!? $completed $editing $deleted") //   CSS  .deleted{display:none} 


وسوف تعمل. ولكن سيكون الغش. بالإضافة إلى ذلك ، إلى أسفل الخط ، سيكون لدينا مرشحات وعدادات للحالات النشطة والمكتملة (ما هو في #footer ). لذلك ، من الأفضل إزالة الكائن فورًا من قائمة المهام بصراحة "جسديًا". أي أننا نحتاج إلى القدرة على تعديل المصفوفة نفسها ، والتي تلقيناها مبدئيًا من الخادم - مما يعني أن هذه المصفوفة يجب أن تصبح أيضًا متغير حالة. دعونا ندعو لها $todos .

يتمثل نطاق متغير $todos في تحديد سلف مشترك لكل العناصر التي ستصل إلى هذا المتغير. وسيتم الوصول إليه بواسطة INPUT#new-todo من #footer ، والعدادات من #footer ، وفي الواقع UL#todo-list . الجد المشترك لكل منهم هو العنصر الرئيسي للقالب ، #todoapp . لذلك ، في قاعدة د $todos المتغير $todos . في نفس المكان ، نقوم على الفور بتحميل البيانات من الخادم إليها. UL#todo-list قائمة UL#todo-list ، سنكون الآن أيضًا منها:

 '#todoapp'.d("$todos=todos:query" //   $todos      ... ,'UL#todo-list'.d("*@ $todos" //     $todos 


هذا مهم. إذا لم يتم تحميل قائمة المهام فجأة - فمن المحتمل تمامًا أن يقوم شخص ما بحذفها جميعًا (هذا خادم عام ، ويمكن أن يحدث أي شيء هناك).
في هذه الحالة ، يرجى الانتقال إلى مثال كامل المواصفات وإنشاء عدد قليل من الحالات للتجربة.

نحن ننظر . هنا $todos الإعلان عن $todos في قاعدة #todoapp للعنصر #todoapp وتتم تهيئته على الفور بالبيانات اللازمة. يبدو أن كل شيء يعمل ، ولكن ظهرت ميزة واحدة غير سارة. إذا كان الخادم يستجيب للطلب لفترة طويلة (يسمح لك Chrome بمحاكاة هذا الموقف: في علامة تبويب "الشبكة" من أدوات المطورين ، يمكنك اختيار أوضاع مختلفة لمحاكاة الشبكات البطيئة) ، ثم إصدارنا الجديد من التطبيق حتى يكتمل الطلب - لا يبدو سوى بعض CSS التحف. بالتأكيد لن تضيف هذه الصورة حماسة للمستخدم. على الرغم من أن الإصدار السابق لم يعان من هذا - إلى أن يتم استلام البيانات على الصفحة ، إلا أن القائمة نفسها كانت مفقودة ، ولكن ظهرت عناصر أخرى على الفور ، دون انتظار البيانات.

هذا هو الشيء. كما تتذكر ، محول :query غير متزامن. يتم التعبير عن عدم التزامن هذا في أنه إلى أن يتم إكمال الطلب ، يتم حظر تنفيذ القاعدة الحالية فقط ، أي إنشاء العنصر الذي يحتاج في الواقع إلى البيانات المطلوبة (وهو منطقي). لا يتم حظر إنشاء عناصر أخرى. لذلك ، عندما وصلت UL#todo-list إلى الخادم ، تم حظرها فقط ، ولكن ليس #footer وليس #footer ، والتي تم رسمها على الفور. الآن #todoapp كلها تنتظر إكمال الطلب.

9. تأخر تحميل البيانات


لتصحيح الموقف وتجنب حظر العناصر غير المرتبطة ، سنؤجل التحميل الأولي للبيانات حتى يتم رسم كل شيء بالفعل. للقيام بذلك ، لن نقوم فوراً بتحميل البيانات في متغير $todos ، ولكن أولاً سنقوم ببساطة بتهيئتها باستخدام "لا شيء"

 '#todoapp'.d("$todos=" //   $todos    "" 


لذلك لن تمنع أي شيء وسيعمل القالب بالكامل على الخروج - وإن كان الآن مع "قائمة مهام" فارغة. ولكن الآن ، مع شاشة أولية مملة ، يمكنك تعديل $todos بأمان عن طريق تحميل قائمة $todos عليها. للقيام بذلك ، أضف هذا السليل إلى #todoapp

  ,'loader' .u("$todos=todos:query") //  $todos,       .d("u") //   (u-)    


يحتوي هذا العنصر على قاعدة u تبدو تمامًا مثل قاعدة الحظر التي رفضناها ، ولكن يوجد اختلاف أساسي واحد.
واسمحوا لي أن أذكرك بأن قاعدة d (من أسفل ) هي قاعدة توليد العناصر التي يتم تنفيذها عند بناء القالب من الأعلى إلى الأسفل ، من الأصل إلى الأولاد ؛ و u-rules (من الأعلى ) هي قواعد رد فعل يتم تنفيذها استجابة لحدث ينبثق من الأسفل إلى الأعلى ، من التابع إلى الأب.
لذلك ، إذا تم تعيين شيء (بما في ذلك "لا شيء") لمتغير في القاعدة d ، فهذا يعني إعلانه وتهيئته في نطاق هذا العنصر ونسله (يتم تطبيق النطاقات المتداخلة في dap ، كما في JS ). التعيين في القواعد العليا يعني تعديل متغير تم الإعلان عنه سابقًا في النطاق. يسمح إعلان وتهيئة المتغيرات في القاعدة d للوالد بتمرير المعلومات الضرورية للبناء إلى المتحدرين من التسلسل الهرمي ، ويسمح التعديل للوالد بتمرير التحديثات إلى هذه المعلومات وبالتالي الشروع في إعادة هيكلة مناسبة لجميع العناصر التي تعتمد عليها.

عنصر loader ، كونه سليل #todoapp ، في قاعدة u يُعدل المتغير $todos ، #todoapp بتحميل البيانات من الخادم فيه ، مما يؤدي إلى التجديد التلقائي لجميع عناصر المستهلك في هذا المتغير (وفقط منهم ، وهو أمر مهم!). مستهلكو المتغير هم عناصر تحتوي قواعدها d على هذا المتغير كـ rvalue ، أي أولئك الذين قرأوا هذا المتغير (مع مراعاة النطاق) عند البناء.

لدينا الآن مستهلك واحد لمتغير $todos - قائمة UL#todo-list ، والتي ، وفقًا لذلك ، سيتم إعادة بنائها بعد تحميل البيانات.

  ,'UL#todo-list'.d("*@ $todos" //  ,   $todos 


لذلك ، لدينا الآن قائمة #todoapp هي متغير حالة في #todoapp ، بينما لا يتم حظر العرض الأولي للقالب.

10. الحذف وإضافة المهام


الآن يمكننا تعديل $todos بكل طريقة. لنبدأ بإزالة العناصر. لدينا بالفعل زر عبر BUTTON.destroy ، والذي يرسل حتى الآن طلبات إزالة الخادم

  ,'BUTTON.destroy'.ui("(@method`DELETE .url:dehttp):query") 


من الضروري التأكد من حذف الكائن المقابل أيضًا من متغير $todos - وبما أن هذا سيكون تعديلًا ، فسيتم تلقائيًا إعادة بناء UL#todo-list ، كمستهلك لهذا المتغير ، ولكن بدون العنصر المحذوف.

في حد ذاته ، لا تقدم dap أي وسيلة خاصة لمعالجة البيانات. يمكن كتابة التلاعب بشكل مثالي في وظائف في JS ، وقواعد dap تقوم ببساطة بتسليم البيانات إليها والتقاط النتيجة. نكتب وظيفة JS لإزالة كائن من صفيف دون معرفة رقمه. على سبيل المثال ، هذا:

 const remove = (arr,tgt)=> arr.filter( obj => obj!=tgt ); 


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

لجعل هذه الوظيفة في متناول من قواعد dap ، تحتاج إلى إضافتها إلى قسم .FUNC ، ولكن قبل ذلك نقرر كيف نريد أن نسميها. ربما يكون الخيار الأبسط في هذه الحالة هو الاتصال به من المحول الذي يقبل الكائن { todos, tgt } وإرجاع صفيف تمت تصفيته

 .FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''), //        remove: x => remove(x.todos,x.tgt) //     } }) 


ولكن لا يوجد شيء يمنعك من تحديد هذه الوظيفة مباشرة داخل. .FUNC (لقد قلت بالفعل أن .FUNC هو في الواقع طريقة JS عادية ، وسيطة هي كائن JS منتظم؟)

 .FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''), remove: x => x.todos.filter( todo => todo!=x.tgt ) } }) 


الآن يمكننا الوصول إلى هذا المحول من قواعد dap

  ,'BUTTON.destroy' .ui("$todos=($todos $@tgt):remove (@method`DELETE .url:dehttp):query") 


هنا ، نقوم أولاً بتكوين كائن ، في تدوين JS ، يطابق { todos, tgt:$ } ، .FUNC إلى :remove converter الموصوفة في .FUNC ، .FUNC النتيجة التي تمت تصفيتها إلى $todos ، وبالتالي تعديلها. هنا $ هو سياق بيانات العنصر ، ذلك الكائن التجاري من صفيف $todos الذي تم بناء القالب عليه. بعد الرمز @ ، تتم الإشارة إلى الاسم المستعار للوسيطة. إذا غابت @ ، فسيتم استخدام اسم الوسيطة الخاص. هذا يشبه الابتكار ES6 الأخيرة - اختصار الممتلكات .

وبالمثل ، نضيف حالة جديدة إلى القائمة باستخدام عنصر INPUT#new-todo وطلب POST

  ,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$=(#.value@title) (@method`POST todos@url headers $):query $todos=($todos $@tgt):insert #.value=") ... .FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''), remove: x => x.todos.filter( todo => todo!=x.tgt ), //     insert: x => x.todos.concat( [x.tgt] ) //     } }) 


تتضمن قاعدة رد فعل عنصر INPUT#new-todo لحدث UI قياسي (بالنسبة لعناصر INPUT ، يعتبر حدث change هو dap القياسي): قراءة إدخال المستخدم من خاصية value هذا العنصر ، وتشكيل سياق $ محلي مع هذه القيمة كحقل .title ، إرسال سياق $ إلى الخادم باستخدام طريقة POST ، وتعديل صفيف $todos عن طريق إضافة السياق $ كعنصر جديد وأخيراً مسح خاصية value لعنصر INPUT .

هنا ، قد يتساءل القارئ الصغير: لماذا استخدم concat() عند إضافة عنصر إلى صفيف ، إذا كان يمكن القيام بذلك باستخدام push() العادي push() ؟ سوف يفهم القارئ المتمرس على الفور الأمر ، ويكتب إجابته في التعليقات.

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

في الواقع ، يتم تضمين كافة المعلومات الضرورية عن الحالة في استجابة الخادم لطلب POST ، وسيكون من الأصح إنشاء كائن أعمال جديد ليس فقط من إدخال المستخدم ، ولكن من استجابة الخادم ، وإضافة هذا الكائن إلى $todos - مع كل ما تم توفيره معلومات الخادم ، بما في ذلك الحقل .url

  ,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$todos=($todos (@method`POST todos@url headers (#.value@title)):query@tgt ):insert #.value=") 


نحن ننظر - حسنا ، الآن كل شيء يجري العمل بشكل صحيح. الإخطارات إلى الخادم حول تحرير الحالات التي تم إنشاؤها حديثًا تسير على ما يرام.

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

  //    , :query   .ui("$=(#.value@title) (@method`POST todos@url headers $):query $todos=($todos $@tgt):insert #.value=") 


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

ولكن ، نظرًا لأننا نستخدم نتيجة طلب POST - إسنادها إلى سياق $ - علينا الانتظار حتى يكتمل. لذلك ، تحتاج إلى البحث عن طريقة أخرى لتعديل $todos قبل تنفيذ طلب POST. الحل: لا يزال ، عليك أولاً إنشاء كائن أعمال جديد وإضافته فورًا إلى $todos ، والسماح لقائمة السحب ، وبعد ذلك فقط ، بعد التقديم ، إذا لم يكن لدى الشركة .url (أي ، تم إنشاء النشاط التجاري للتو) ، قم بتنفيذ طلب POST ، ثم فرض نتائجه على سياق البيانات لهذه الحالة.

لذلك ، أولاً نضيف فقط فارغًا يحتوي فقط على .title إلى .title

  ,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$todos=($todos (#.value@title)@tgt):insert #.value=") 


UL#todo-list > LI قاعدة إنشاء عنصر UL#todo-list > LI تحتوي بالفعل على a! بدء قاعدة بعد رسم العنصر أولاً. هناك يمكننا أن نضيف إطلاق طلب POST في غياب .url . لحقن حقول إضافية في السياق ، لدى dap المشغل &

  .a("!? $completed $editing; ? .url:!; & (@method`POST todos@url headers $):query") 


نحن ننظر . شيء اخر حتى مع وجود شبكة بطيئة ، يتم تحديث قائمة المهام على الفور ، ويحدث إشعار الخادم وتحميل البيانات المفقودة في الخلفية ، بعد رسم القائمة المحدّثة.

11. داو الجميع!


في عنصر #header يوجد زر للتثبيت / إعادة الضبط الشامل لعلامة الاكتمال لجميع الحالات في القائمة. للتخصيص الجماعي للقيم في حقول عناصر المصفوفة ، نكتب فقط محولًا آخر :assign ، وتطبيقه على $todos خلال النقر على INPUT#toggle-all

  ,'INPUT#toggle-all type=checkbox' .ui("$todos=($todos (#.checked@completed)@src):assign") ... assign: x => x.todos && x.todos.map(todo => Object.assign(todo,x.src)) 


في هذه الحالة ، نحن مهتمون فقط بحقل .completed ، لكن من السهل أن نرى أنه مع مثل هذا المحول يمكنك تغيير قيم أي حقول عناصر الصفيف بشكل كبير.
حسنًا ، في صفيف $todos تبديل $todos ، والآن نحتاج إلى إخطار الخادم بالتغييرات التي تم إجراؤها. في المثال الأصلي ، يتم ذلك عن طريق إرسال طلبات PATCH لكل حالة - ليست استراتيجية فعالة للغاية ، لكنها لم تعد تعتمد علينا. حسنًا ، في كل حالة نرسل طلب تصحيح

  .ui("*@ $todos=($todos (#.checked@completed)@src):assign; (@method`PATCH .url:dehttp headers (.completed)):query") 


نحن ننظر : نقرة على داو المشتركة محاذاة جميع daws الفردية ، ويتم إخطار الخادم عن طريق طلبات PATCH المناسبة. المعايير.

12. تصفية الحالات بناء على الانتهاء


بالإضافة إلى قائمة المهام الفعلية ، يجب أن يكون التطبيق قادرًا أيضًا على تصفية الحالات عن طريق علامة الإكمال وإظهار عدادات المهام المكتملة وغير المكتملة. بالطبع ، من أجل التصفية سنكون تافهين لاستخدام نفس طريقة filter() التي توفرها JS نفسها.

ولكن عليك أولاً التأكد من أن حقل .completed لكل حالة صحيح دائمًا ، وعند النقر فوق يتم تحديث daw الفردية للحالة مع متغير $completed . في السابق ، لم يكن هذا مهمًا بالنسبة لنا ، ولكن الآن سيكون الأمر كذلك.

  ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=(.completed=$completed=#.checked) $recount=()") //  .completed        


النقطة المهمة هنا هي أن سياق البيانات لكل حالة هو كائن الحالة نفسه ، والذي يقع في صفيف $todos . ليس نسخة واحدة ، أو البناء ذات الصلة ، ولكن الكائن نفسه. وجميع المكالمات إلى الحقول. العنوان ، .completed يطبق url - كلا القراءة والكتابة - مباشرة على هذا الكائن. لذلك ، لكي تعمل تصفية الصفيف $todos بشكل صحيح ، نحتاج إلى إكمال انعكاس الحالة ليس فقط في daw على الشاشة ، ولكن أيضًا في الحقل .completed الخاص بكائن .completed .

لإظهار الحالات فقط مع وجود علامة الاكتمال اللازمة. completed في القائمة ، سنقوم ببساطة بتصفية $todos وفقًا للمرشح المحدد. المرشح المحدد هو ، حسب ما تفكر فيه ، متغير حالة آخر لتطبيقنا ، وسنسميه: $filter . لتصفية $todos وفقًا $filter المحدد $filter دعنا ننتقل إلى الصورة المصغرة ونضيف فقط محولًا آخر من النموذج {list، filter} => القائمة التي تمت تصفيتها ، وسنتخذ الأسماء ووظائف التصفية من "المصفوفة الترابطية" (أي ، JS العادية) كائن) todoFilters

 const todoFilters={ "All": null, "Active": todo => !todo.completed, "Completed": todo => !!todo.completed }; '#todoapp'.d("$todos= $filter=" //   $filter ... ,'UL#todo-list'.d("* ($todos $filter):filter" ... ,'UL#filters'.d("* filter" //  filter      .DICT ,'LI' .d("! .filter") .ui("$filter=.") //    "$filter=.filter" ) ... .DICT({ ... filter: Object.keys(todoFilters) //["All","Active","Completed"] }) .FUNC({ convert:{ ... filter: x =>{ const a = x.todos, f = x.filter && todoFilters[x.filter]; return a&&f ? a.filter(f) : a; } } }) 


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

13. عدادات الحالات المكتملة والفعالة.


لإظهار عدادات الحالات المكتملة والفعالة ، ما $todos سوى تصفية $todos باستخدام المرشحات المناسبة وإظهار أطوال المصفوفات الناتجة

  ,'#footer'.d("$active=($todos @filter`Active):filter $completed=($todos @filter`Completed):filter" ,'#todo-count'.d("! (active $active.length)format") //  length    active ... ,'#clear-completed'.d("! (completed $completed.length)format") ) ... .DICT({ ... active: "{length} items left", completed: "Clear completed items ({length})" }) 


في هذا النموذج ، تُظهر العدادات القيم الصحيحة في التمهيد ، لكن لا تستجيب للتغييرات اللاحقة في إكمال الشؤون (عند النقر فوق daws). الحقيقة هي أن النقرات على الغربان ، وتغيير حالة كل حالة على حدة ، لا تغير حالة $todos - تعديل عنصر الصفيف ليس تعديلًا للصفيف نفسه. لذلك ، نحتاج إلى إشارة إضافية حول الحاجة إلى إعادة تسجيل الحالات. يمكن أن تكون مثل هذه الإشارة متغيرًا إضافيًا للحالة يتم تعديله في كل مرة تكون فيها عملية إعادة الحساب مطلوبة. نسميها $recount . d- , , #footer — d-

 '#todoapp'.d("$todos= $filter= $recount=" //  $recount     ... ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=(.completed=$completed=#.checked) $recount=()") //  $recount    ... ,'#footer'.d("$active=($todos @filter`Active):filter $completed=($todos @filter`Completed):filter $recount" //  $recount 


, .

14. .


TodoMVC , — . , , , DELETE- — $completed . , $todos , $active

  ,'#clear-completed' .d("! (completed $completed.length)format") .ui("$todos=$active; *@ $completed; (@method`DELETE .url:dehttp):query") 


: , . Network .

15.


. #. #- — . URL .

location.hash urlhash , , a- #todoapp ( ), $filter

 .a("urlhash $filter") 


$filter hashchange - :urlhash , location.hash ( #)

 .d("$todos= $filter=:urlhash $recount=" .e("hashchange","$filter=:urlhash") 


hashchange #- . , - window document.body . #todoapp , d- listen , window

 '#todoapp' .a("urlhash $filter") .e("hashchange","$filter=:urlhash") .d("$todos= $filter=:urlhash $recount=; listen @hashchange" 


: , , #Active , #All , #Completed . . . , , — . , —

  ,'UL#filters'.d("* filter" ,'LI'.d("" ,'A'.d("!! (`# .filter)concat@href .filter@") ) ) 


, !? , CSS- selected , .filter $filter

  ,'A'.d("!! (`# .filter)concat@href .filter@; !? (.filter $filter)eq@selected") 


dap- ( ) , .

16.


, , head HTML-

  [ui=click]{cursor:pointer} 


, .

, ! «todos». , , - , «todos» «dap todos»

  ,'H1'.d("","dap todos") 


. , ( ).

في الختام


, , dap- — «, », « , » .. هذا في الواقع ليس هو الحال. . , , , .

, , .

Source: https://habr.com/ru/post/ar481680/


All Articles