دب في العمل. كتابة TodoMVC. الجزء 1

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

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

لم أقم بدراسة ودراسة الوصف الرسمي للمشكلة ، لكنني قررت ببساطة عكس أحد الأمثلة. الواجهة الخلفية لهذه المقالة ليست مثيرة للاهتمام بالنسبة لنا ، لذلك لن نكتبها بأنفسنا ، ولكننا نستخدم واحدة منها جاهزة من الموقع www.todobackend.com ، ومن هناك سنتخذ مثالاً للعميل وملف CSS قياسي.

لاستخدام ملف dap ، لن تحتاج إلى تنزيل أي شيء وتثبيته. لم يتم npm install وهذا كل شيء. ليس مطلوبًا إنشاء أي مشاريع بهيكل دليل محدد وبيانات وخصائص أخرى لنجاح تكنولوجيا المعلومات. يكفي محرر النص والمتصفح. لتصحيح طلبات XHR ، قد تحتاج أيضًا إلى خادم ويب - خادم بسيط بما فيه الكفاية ، مثل هذا الامتداد لمتصفح Chrome. ستتألف الواجهة الأمامية بالكامل من ملف .html واحد (بالطبع ، يشير إلى البرنامج النصي لمحرك dap وإلى ملف TodoMVC CSS القياسي)

لذلك ، من الصفر.

1. إنشاء ملف. html


 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Todo -- dap sample</title> <link rel="stylesheet" href="https://www.todobackend.com/client/css/vendor/todomvc-common.css"/> <script src="https://dap.js.org/0.4.js"></script> </head> <body> <script> //   dap </script> </body> </html> 

إعداد html المعتاد الذي ندرج فيه ملف CSS ، يرجى تقديمه من خلال الموقع www.todobackend.com ومحرك dap ، وهو ما لا يوفره الموقع dap.js.org.

2. انسخ بنية DOM للمثال الأصلي


لاستخدام ملف CSS القياسي دون تعديلات ، سنلتزم بنفس بنية DOM مثل المثال الأصلي . افتحه في متصفح Chrome ، واضغط على Ctr + Shift + I ، وحدد علامة تبويب "العناصر" وشاهد أن التطبيق نفسه موجود في section id="todo-app"> element section id="todo-app">



من خلال فتح هذه الشجرة الفرعية واحدة تلو الأخرى ، نعيد كتابة هيكلها في ملف .html الخاص بنا. الآن نحن ببساطة نرسم بطريقة سريعة ، ولا نكتب الشفرة ، لذلك نكتب تواقيع العناصر في "علامات اقتباس مفردة" ، وفي أقواس أطفالهم. إذا لم يكن هناك أطفال ، نرسم أقواس فارغة. نحن نراقب المؤشرات وتوازن الأقواس.

 //   dap '#todoapp'( '#header'( 'H1'() 'INPUT#new-todo placeholder="What needs to be done?" autofocus'() ) '#main'( '#toggle-all type=checkbox'() 'UL#todo-list'( 'LI'( 'INPUT.toggle type=checkbox'() 'LABEL'() 'BUTTON.destroy'() ) ) ) '#footer'( '#todo-count'() 'UL#filters'( 'LI'() ) '#clear-completed'() ) ) 

ملاحظة: نكرر العناصر (على سبيل المثال ، هنا عناصر LI ) نكتب إلى الهيكل مرة واحدة ، حتى إذا كان هناك العديد منها في الأصل ؛ من الواضح أن هذه صفائف من نفس النمط.

أعتقد أن تنسيق التوقيع أمر مفهوم لأي شخص كتب HTML و CSS بأيديهم ، لذلك لن أتناوله بالتفصيل في الوقت الحالي. لا أستطيع إلا أن أقول إن العلامات مكتوبة بحروف كبيرة ، وأن عدم وجود علامة يكافئ وجود علامة DIV. يرجع السبب في وفرة # -elements (ذات المعرف) هنا إلى تفاصيل ملف CSS المضمّن ، والذي يستخدم بشكل رئيسي محددات الهوية.

3. تذكر أن برنامج dap هو Javascript


String.prototype من الأقواس غير الضرورية في الكود ، يقوم محرك String.prototype العديد من الطرق مباشرةً في String.prototype (أعرف أن تطبيق الأساليب في الكائنات القياسية هو ayahay ، لكن ... حسنًا ، لقد مررنا) ، والذي يحول سلسلة التوقيع إلى dap -shablon. إحدى هذه الطرق هي .d(rule, ...children) . الحجة الأولى التي يتطلبها الأمر هي قاعدة جيل (قاعدة د ) ، وبقية الحجج عبارة عن عدد تعسفي من الأطفال.

بناءً على هذه المعرفة الجديدة ، نضيف الكود الخاص بنا بحيث يكون لدينا تسلسل. بدلاً من كل قوس فتح .d("" ، وقبل كل عرض أسعار فردي ، باستثناء الفاصلة الأولى ، هناك فاصلة. Life hack: يمكنك استخدام الاستبدال التلقائي.

 '#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("" ,'LI'.d("" ,'INPUT.toggle type=checkbox'.d("") ,'LABEL'.d("") ,'BUTTON.destroy'.d("") ) ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) ) 

فويلا! حصلنا على مجموعة من المكالمات إلى طريقة .d ، وهي جاهزة للتحويل إلى قالب dap. السلاسل الفارغة "" هي بذور قواعد د المستقبلية ، والطفل هي حجج مفصولة بفواصل. من الناحية الرسمية ، هذا برنامج dap صالح ، على الرغم من أنه ليس بالكامل بعد مع العادم الذي نحتاجه. ولكن يمكن بالفعل إطلاقه! للقيام بذلك ، بعد قوس الجذر إغلاق ، إضافة طريقة .RENDER() . هذه الطريقة ، كما يوحي اسمها ، تعرض القالب الناتج.

لذلك ، في هذه المرحلة ، لدينا ملف. html به هذا المحتوى:

 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Todo -- dap sample</title> <link rel="stylesheet" href="https://www.todobackend.com/client/css/vendor/todomvc-common.css"/> <script src="https://dap.js.org/0.4.js"></script> </head> <body> <script> '#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("" ,'LI'.d("" ,'INPUT.toggle type=checkbox'.d("") ,'LABEL'.d("") ,'BUTTON.destroy'.d("") ) ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) ) .RENDER() //   dap   </script> </body> </html> 

يمكنك فتحه في متصفح للتأكد من أن عناصر DOM يتم إنشاؤها ، وأنماط CSS مطبقة ، ويظل فقط لملء هذا القالب بالبيانات.

4. الحصول على البيانات


نذهب إلى الصفحة الأصلية ، ونفتح علامة التبويب الشبكة في الأدوات ، ونشغل مرشح XHR ، ونرى من أين تأتي البيانات وبأي شكل.





حسنا حسنا. يتم تأليف قائمة المهام مباشرة من todo-backend-express.herokuapp.com كمجموعة json من الكائنات. رائع.

لتلقي البيانات ، يحتوي ملف dap على محول مضمن :query الذي "يحول" عنوان URL بشكل غير متزامن إلى البيانات الواردة منه. لن نكتب عنوان URL نفسه مباشرةً في القاعدة ، لكننا سنقوم بتعيينه باستخدام todos الثابت ؛ عندها سيبدو تصميم التنقيب عن البيانات بالكامل كما يلي:

 todos:query 

واكتب ثابت todos نفسه في القاموس - في قسم .DICT ، مباشرة قبل .RENDER() :

 '#todoapp'.d("" ... ) .DICT({ todos : "https://todo-backend-express.herokuapp.com/" }) .RENDER() 

بعد تلقي صفيف todos ، نقوم ببناء قائمة مهام منها: في كل حالة نأخذ الاسم من حقل .title إلى عنصر LABEL ، ومن الحقل .completed نأخذ علامة "اكتمال" - والكتابة إلى خاصية checked من عنصر checked INPUT.toggle . يتم ذلك مثل هذا:

  ,'UL#todo-list'.d("*@ todos:query" //  *       ,'LI'.d("" ,'INPUT.toggle type=checkbox'.d("#.checked=.completed") // #  " " ,'LABEL'.d("! .title") //  !      ,'BUTTON.destroy'.d("") ) ) 

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



حان الوقت لمشاهدة صفحتنا عبر http - باستخدام أي خادم ويب محلي. حسنًا ، أو إذا لم تكن مستعدًا لكتابة ملف dap بيديك ، فراجع الإصدارات المتسلسلة من الصفحة باستخدام روابطي (لا تنس النظر إلى المصدر - في Chrome ، يتم ذلك باستخدام Ctrl + U)

لذلك ، نذهب إلى صفحتنا على الموقع http: // ونرى أن البيانات قادمة ، يتم إنشاء القائمة. ! ممتاز لقد أتقنت بالفعل * والمشغلين ! ، المحول :query ، الثوابت والوصول إلى حقول عنصر الصفيف الحالي. انظر مرة أخرى إلى الكود الناتج. هل لا يزال يبدو غير قابل للقراءة بالنسبة لك؟

5. أضف الدولة


ربما تكون قد حاولت بالفعل النقر فوق علامات الاختيار في قائمة المهام. تغير مربعات الاختيار نفسها اللون ، ولكن على عكس الأصل ، لا يغير العنصر الأصل LI أسلوبه (يجب أن تصبح "المهمة المكتملة" رمادية ومُشطب عليها ، لكن هذا لا يحدث) - لا تغير الأشياء حالتها. لكن هذه العناصر لا تملك أي ولاية ، وبالتالي لا يمكنها تغييرها. الآن سوف نصلحها.

أضف حالة "الاكتمال" إلى عنصر LI . للقيام بذلك ، قم بتعريف متغير الحالة $completed في قاعدة d. بالنسبة INPUT.toggle ، الذي يمكنه تغيير هذه الحالة ، سنقوم بتعيين قاعدة رد فعل مناسبة (قاعدة ui ) ، والتي ستقوم بتعيين المتغير $completed وفقًا للعلم الذي checked ("يتم تشغيل daw"). وفقًا لحالة $completed عنصر LI إما أن يمكّن أو يعطل فئة CSS "مكتملة".

  ,'UL#todo-list'.d("*@ todos:query" ,'LI'.d("$completed=.completed"//  ,    .completed ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") //       .ui("$completed=#.checked") //    $completed ,'LABEL'.d("! .title") ,'BUTTON.destroy'.d("") ) .a("!? $completed") //     $completed,    css- completed ) 

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

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

  ,'LI'.d("$completed=.completed; a!" //      $completed    a- 

نحن ننظر . حسنا. مع حالة $completed أحسب. الحالات المكتملة مصممة بطريقة صحيحة سواء في التمهيد الأولي أو أثناء التبديل اليدوي اللاحق.

6. تحرير أسماء الحالات


العودة إلى الأصل . من خلال النقر المزدوج على اسم الحالة ، يتم تنشيط وضع التحرير ، حيث يمكن تغيير هذا الاسم. هناك يتم تنفيذه بطريقة مخفية تمامًا قالب وضع العرض "عرض" (مع داو ، وعنوان وزر حذف) ، ويتم عرض عنصر INPUT class="edit" . سنفعل ذلك بطريقة مختلفة بعض الشيء - سنقوم بإخفاء عنصر LABEL فقط ، نظرًا لأن العنصرين الآخرين لا يتعارضان مع عمليات التحرير الخاصة بنا. فقط أضف فئة view إلى عنصر LABEL .

بالنسبة لحالة "التحرير" ، حدد متغير $editing في عنصر LI . في البداية ، تتم إعادة ضبط (الحالة) ، dblclick بواسطة dblclick على عنصر LABEL ، ويتم إيقاف تشغيله عندما يكون عنصر INPUT.edit خارج نطاق التركيز. لذلك نكتب:

  ,'LI'.d("$completed=.completed $editing=; a!" //       ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$completed=#.checked") ,'LABEL.view' .d("? $editing:!; ! .title") //  $editing ,     .e("dblclick","$editing=`yes") //  dblclick  $editing ,'INPUT.edit' .d("? $editing; !! .title@value") //  $editing  .ui(".title=#.value") //  .title   change (ui     INPUT) .e("blur","$editing=") //  $editing   blur ,'BUTTON.destroy'.d("") ).a("!? $completed $editing") //   $completed  $editing  css-  'LI' 

الآن يمكننا تعديل أسماء الحالات.

7. إرسال البيانات إلى الخادم


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



يتم إرسال التغييرات إلى الخادم باستخدام طريقة PATCH مع عنوان URL معين للنموذج http://todo-backend-express.herokuapp.com/28185 ، والذي من الواضح أنه فريد لكل حالة. تتم الإشارة إلى عنوان URL هذا بواسطة الخادم في حقل .url لكل حالة في القائمة. أي أن كل ما هو مطلوب منا لتحديث الحالة على الخادم هو إرسال طلب PATCH إلى العنوان المحدد في الحقل .url ، مع تغيير البيانات في تنسيق JSON:

  ,'INPUT.edit' .d("? $editing; !! .title@value") .ui(".title=#.value; (@method`PATCH .url (@Content-type`application/json)@headers (.title):json.encode@body):query") .e("blur","$editing=") 

هنا نستخدم نفس المحول :query ، ولكن في إصدار أكثر تفصيلا. عندما :query يتم تطبيق :query على سلسلة بسيطة ، يتم التعامل مع هذه السلسلة كعنوان URL ويتم تنفيذ طلب GET. إذا :query يتلقى :query كائنًا معقدًا ، كما في هذه الحالة ، فإنه يعامله باعتباره وصفًا مفصلاً للطلب الذي يحتوي على الحقول .method و .url و .headers و .headers ، وينفذ الطلب وفقًا لها. هنا ، مباشرة بعد تحديث .title نرسل إلى الخادم طلب PATCH مع هذا .title المحدث

ولكن هناك فارق بسيط. نحصل على الحقل .url من الخادم ، يبدو كالتالي: http://todo-backend-express.herokuapp.com/28185 ، وهذا يعني أن بروتوكول http: // يتم تشفيره بداخله إذا كان عميلنا مفتوحًا أيضًا عبر http: // ثم كل شيء على ما يرام. ولكن إذا كان العميل مفتوحًا عبر https: // ، فثمة مشكلة: لأسباب أمنية ، يقوم المتصفح بحظر http-traffic من مصدر https.

يتم حلها ببساطة: إذا قمت بإزالة البروتوكول من .url ، فسوف يمر الطلب عبر بروتوكول الصفحة. لذلك دعونا نفعل ذلك: اكتب المحول المناسب - dehttp ، dehttp .url عبره. تم تحديد المحولات المخصصة (والوظائف الأخرى) في قسم .FUNC :

  .ui(".title=#.value; (@method`PATCH .url:dehttp (@Content-type`application/json)@headers (.title):json.encode@body):query") ... .FUNC({ convert:{ //  -         dehhtp: url=>url.replace(/^https?\:/,'')//    URL } }) 

من المنطقي أيضًا وضع كائن الرؤوس في القاموس بحيث يمكن استخدامه في الاستعلامات الأخرى:

  .ui(".title=#.value; (@method`PATCH .url:dehttp headers (.title):json.encode@body):query") ... .DICT({ todos : "//todo-backend-express.herokuapp.com/", headers: {"Content-type":"application/json"} }) 

حسنًا ، بالنسبة لكامل فنغ شوي ، سنستخدم خاصية مفيدة أخرى للمحول :query - ترميز تلقائي لجسم الطلب في json وفقًا لرأس Content-type:application/json . نتيجة لذلك ، ستبدو القاعدة كما يلي:

  .ui(".title=#.value; (@method`PATCH .url:dehttp headers (.title)):query") 

لذا انظر . حسنًا ، أسماء الحالات تتغير الآن ليس فقط في المتصفح ، ولكن أيضًا على الخادم. ولكن! ليس فقط اسم القضية يمكن أن يتغير ، ولكن أيضًا حالته المكتملة - completed . لذلك ، يجب أيضًا إرسالها إلى الخادم.
يمكنك إضافة طلب PATCH نفسه إلى عنصر INPUT.toggle ، فقط أرسل (.completed) بدلاً من (.completed) :

  ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$completed=#.checked; (@method`PATCH .url:dehttp headers (.completed:?)):query") 

ويمكنك وضع طلب التصحيح هذا "خارج الأقواس":

  ,'LI'.d("$completed=.completed $editing= $patch=; a!" // $patch - ""   ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=($completed=#.checked)") //   $patch  completed ,'LABEL.view' .d("? $editing:!; ! .title") .e("dblclick","$editing=`yes") ,'INPUT.edit' .d("? $editing; !! .title@value") .ui("$patch=(.title=#.value)") //   $patch  title .e("blur","$editing=") ,'BUTTON.destroy'.d("") ) .a("!? $completed $editing") //  $patch  ,   ,   .u("? $patch; (@method`PATCH .url:dehttp headers $patch@):query $patch=") 

هذا هو الشيء. تنتمي قواعد رد الفعل إلى مجموعة "القواعد العليا" ، والتي يتم تنفيذها "من الأسفل إلى الأعلى" - من السليل إلى الأصل ، إلى الجذر نفسه (يمكن مقاطعة هذا التسلسل إذا لزم الأمر). هذا يشبه إلى حد ما الأحداث المنبثقة في DOM. لذلك ، يمكن تعيين بعض أجزاء من رد الفعل المشترك لعدة أحفاد لسلف مشترك.

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

ننظر : الآن يتم إرسال كل من تغييرات الاسم وتغييرات الحالة إلى الخادم.

في المقالة التالية ، إذا كنت مهتمًا ، ففكر في إضافة الحالات وإزالتها وتصفيتها. في غضون ذلك ، يمكنك رؤية النتيجة النهائية وأمثلة أخرى لكود dap على dap.js.org/docs

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


All Articles