Vue.js هو إطار عمل لتطوير تطبيقات الويب. يحتوي على نظام تفاعلية يسمح للمطور بمحاكاة حالة التطبيق وإدارته. ونتيجة لذلك ، عندما تتغير البيانات ، فإنها تنعكس تلقائيًا على واجهة المستخدم ، بينما لا يحتاج المطور إلى الوصول إلى DOM. إذا قمت بإنشاء تطبيقات باستخدام JavaScript خالص أو jQuery ، فهذا يعني أنه يجب عليك الوصول بشكل صريح إلى عناصر DOM وتحديثها من أجل عكس التغييرات في حالة التطبيق في الواجهة ، على سبيل المثال ، عرض بعض البيانات على صفحة الويب.
في حالة المشاريع الكبيرة ، فإن إدارة مزامنة واجهة التطبيق والتطبيق يدويًا ليست مهمة سهلة. يريد مؤلف المادة ، التي نعرضها على انتباهك اليوم ، مشاركة بعض نتائج بحثه التي تهدف إلى مقارنة نسختين من
تطبيق ويب تقدمي باستخدام
Hoodie ، وهي
قائمة تسوق . تمت كتابة النسخة الأساسية من هذا التطبيق في JS خالص (في
هذه المقالة يمكنك العثور على تفاصيل حوله). هنا سيتم عرض ترجمة التطبيق إلى Vue.js مع فحص عابر للسمات الأساسية لهذا الإطار ومع تحليل لما حدث في النهاية.
تطبيق جاهزإذا كنت ترغب في العمل من خلال هذه المادة ، يمكنك تنزيل
شفرة المصدر للتطبيق في JS خالص ومتابعة المؤلف ، ومعالجته باستخدام Vue.
إضافة منتجات إلى القائمة
avaجافا سكريبت
يسمح التطبيق للمستخدم بإضافة منتجات إلى قائمة التسوق الخاصة بهم. يتم ذلك في ملف
index.html
في المجلد
العمومي . الترميز موجود في الأسطر 92-124 من هذا الملف. ها هي ذا.
<div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> <input class="mdl-textfield__input" type="text" id="new-item-name"> <label class="mdl-textfield__label" for="new-item-name">Item Name</label> </div> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> <input class="mdl-textfield__input" type="number" id="new-item-cost"> <label class="mdl-textfield__label" for="new-item-cost">Item Cost</label> </div> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> <input class="mdl-textfield__input" type="number" id="new-item-quantity"> <label class="mdl-textfield__label" for="new-item-quantity">Quantity</label> </div> </div> <div class="mdl-grid center-items"> <button id="add-item" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored"> Add Item </button> </div> </div>
رمز معالجة البيانات وحفظها موجود في الملف
public/js/src/index.js
. تعتبر
saveItems()
في السطر 28 مسؤولة عن جمع القيم من عناصر التحكم المستخدمة لإدخال البيانات وحفظ تلك البيانات. ترتبط هذه الوظيفة بحدث
click
لزر
add-item
الإضافي. هذا هو الرمز المعني.
function saveNewitem() { let name = document.getElementById("new-item-name").value; let cost = document.getElementById("new-item-cost").value; let quantity = document.getElementById("new-item-quantity").value; let subTotal = cost * quantity; if (name && cost && quantity) { hoodie.store.withIdPrefix("item").add({ name: name, cost: cost, quantity: quantity, subTotal: subTotal }); document.getElementById("new-item-name").value = ""; document.getElementById("new-item-cost").value = ""; document.getElementById("new-item-quantity").value = ""; } else { let snackbarContainer = document.querySelector("#toast"); snackbarContainer.MaterialSnackbar.showSnackbar({ message: "All fields are required" }); } } document.getElementById("add-item").addEventListener("click", saveNewitem);
▍Vue
قبل البدء في إعادة صياغة هذا المشروع باستخدام Vue ، تحتاج إلى ربط إطار العمل بالصفحة. في حالتنا ، في ملف
index.html
، يتم ذلك على النحو التالي:
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
بالإضافة إلى ذلك ، تمت إضافة عنصر
<div>
مع
app
المعرف ، والذي سيتضمن جميع عناصر الصفحة الموجودة داخل علامة النص. وذلك لأنه عندما تتم تهيئة مثيل Vue ، يجب إعلام إطار العمل بالجزء الذي يجب إدارته من الصفحة. في هذه الحالة ، نبلغ الإطار بأنه يجب أن يتعامل مع كل ما هو موجود في هذه الكتلة.
لنبدأ الآن ترجمة المشروع إلى Vue. أولاً ، نقوم بتعديل الترميز لاستخدام بعض توجيهات Vue. توجيهات Vue هي سمات خاصة مسبوقة بـ
v-
. إليك ما يبدو عليه الترميز المحدث.
<form v-on:submit.prevent="onSubmit"> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> <input class="mdl-textfield__input" type="text" id="new-item-name" v-model="name"> <label class="mdl-textfield__label" for="new-item-name">Item Name</label> </div> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> <input class="mdl-textfield__input" type="number" id="new-item-cost" v-model.number="cost"> <label class="mdl-textfield__label" for="new-item-cost">Item Cost</label> </div> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> <input class="mdl-textfield__input" type="number" id="new-item-quantity" v-model.number="quantity"> <label class="mdl-textfield__label" for="new-item-quantity">Quantity</label> </div> </div> <div class="mdl-grid center-items"> <button id="add-item" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored"> Add Item </button> </div> </form>
يتم استخدام التوجيه
v-on
used المستخدم هنا للاستماع إلى أحداث DOM. في مقتطف الشفرة أعلاه ، يتم استخدامه في عنصر النموذج للاستماع إلى حدث
submit
. بالإضافة إلى ذلك ، يستخدم معدّل
.prevent
، الذي يخبر توجيه
v-on
لاستدعاء
event.preventDefault()
في الحدث المطلوب. استخدمنا توجيه
v-model
للعناصر المستخدمة لإدخال البيانات. هناك حاجة ، كما تم تطبيقها لتكوين العناصر ، لإنشاء ربط بيانات ثنائي الاتجاه. يسمح هذا للنظام بتحديد الطريقة الصحيحة تلقائيًا لتحديث عنصر بناءً على نوعه. استخدمنا مُعدِّل
.number
لعناصر
cost
quantity
.
يؤدي هذا تلقائيًا إلى تحويل نوع القيمة من حقل الإدخال إلى رقم. يتم ذلك لأنه حتى إذا تم تعيين
type=number
على
type=number
، فسيتم إرجاع القيمة دائمًا كسلسلة. المعدلات المستخدمة هنا أتمتة تنفيذ عمليات التحقق الإضافية التي كان يتعين إجراؤها في السابق بشكل مستقل.
بعد ذلك ، قم بإنشاء ملف جديد ،
index-vue.js
، حيث نضع الشفرة المكافئة للرمز في
index.js
، ولكن باستخدام إمكانات Vue. يظهر رمز هذا الملف أدناه. يؤدي هذا إلى إنشاء مثيل لـ Vue مع الخصائص الضرورية لمعالجة أحداث النموذج وجمع البيانات.
const vm = new Vue({ el: "#app", data: { name: "", cost: "", quantity: "" }, methods: { onSubmit: function(event) { if (this.name && this.cost && this.quantity) { hoodie.store.withIdPrefix("item").add({ name: this.name, cost: this.cost, quantity: this.quantity, subTotal: this.cost * this.quantity }); this.name = ""; this.cost = ""; this.quantity = ""; } else { const snackbarContainer = document.querySelector("#toast"); snackbarContainer.MaterialSnackbar.showSnackbar({ message: "All fields are required" }); } } } });
تم إنشاء مثيل لـ Vue في مقتطف الرمز هذا ، وتم تمرير كائن إليه ، لإخبار Vue بكيفية تكوين التطبيق. تُخبر الخاصية
el
إطار العمل بمُعرّف عنصر DOM ، الذي سيتم التحكم في محتوياته بواسطة Vue ، معتبراً إياه "أراضيه". ضمن هذا العنصر ستأخذ Vue في الاعتبار التوجيهات الخاصة بالإطار (وكل شيء آخر متعلق بـ Vue) ، وفي عملية تهيئة الإطار ، ستقوم بتكوين الارتباطات ومعالجات الأحداث للتطبيق.
تحتوي خاصية
data
على حالة التطبيق. سيتم إضافة جميع الخصائص الموجودة في الكائن المتاح هنا ، عند تهيئة Vue ، إلى نظام استجابة الإطار. إن إجراءات هذا النظام هي التي تؤدي إلى تحديث واجهة المستخدم عند تغيير القيم المرتبطة بـ DOM. على سبيل المثال ، خاصية
name
مرتبطة بعنصر التحكم في
name
باستخدام الأمر
v-model="name"
. يعيّن هذا التوجيه ربط البيانات ثنائي الاتجاه بين الخاصية وعنصر التحكم بطريقة يتم عندها إضافة شيء إلى حقل الإدخال (أو إزالة شيء منه) ، يتم تحديث خاصية
name
. ونتيجة لذلك ، تعكس محتويات حقل الإدخال الحالة الحالية لخاصية
name
.
وبالمثل ، يعمل الربط مع ضوابط أخرى.
تحتوي خاصية
methods
على وظائف. في الكود أعلاه ، يتم
onSubmit()
وظيفة
onSubmit()
، والتي يتم إرفاقها بحدث نموذج
submit()
.
عرض البيانات المحفوظة على الصفحة
avaجافا سكريبت
تحفظ وظيفة
onSubmit
البيانات في غطاء محرك السيارة. بعد ذلك ، نحتاج إلى عرض العناصر المضافة إلى المستودع ، على شكل جدول. في حالة طلب مكتوب بلغة JS العادية ، يبدو الترميز المقابل كما يلي:
<div class="mdl-grid center-items"> <table id="item-table" class="mdl-data-table mdl-js-data-table mdl-shadow--2dp"> <thead> <tr> <th class="mdl-data-table__cell--non-numeric">Item Name</th> <th class="mdl-data-table__cell--non-numeric">Cost</th> <th class="mdl-data-table__cell--non-numeric">Quantity</th> <th class="mdl-data-table__cell">Sub-total</th> <th class="mdl-data-table__cell--non-numeric"> <button class="mdl-button mdl-js-button mdl-button--icon"> <i class="material-icons">delete</font></i> </button> </th> </tr> </thead> <tbody> </tbody> </table> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> <input class="mdl-textfield__input" type="number" id="total-cost" readonly value="0"> <label class="mdl-textfield__label" for="cost">Total Item Cost</label> </div> </div> <script id="item-row" type="text/template"> <tr id='{{row-id}}'> <td class="mdl-data-table__cell--non-numeric">{{name}}</td> <td class="mdl-data-table__cell--non-numeric">{{cost}}</td> <td class="mdl-data-table__cell--non-numeric">{{quantity}}</td> <td class="mdl-data-table__cell">{{subTotal}}</td> <td class="mdl-data-table__cell--non-numeric"> <button class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored" onclick="pageEvents.deleteItem('{{item-id}}')"> <i class="material-icons">remove</font></i> </button> </td> </tr> </script>
سيحتوي الجدول على بيانات مضافة ديناميكيًا ، لذلك نحتاج هنا إلى طريقة لاستبدال العناصر النائبة ببيانات حقيقية وإرفاق ما حدث بـ DOM. في حالتنا ، يتم حل هذه المشكلة باستخدام القوالب الصغيرة.
يعرض الكود التالي العناصر في واجهة المستخدم عند إضافتها.
function addItemToPage(item) { if (document.getElementById(item._id)) return; let template = document.querySelector("#item-row").innerHTML; template = template.replace("{{name}}", item.name); template = template.replace("{{cost}}", item.cost); template = template.replace("{{quantity}}", item.quantity); template = template.replace("{{subTotal}}", item.subTotal); template = template.replace("{{row-id}}", item._id); template = template.replace("{{item-id}}", item._id); document.getElementById("item-table").tBodies[0].innerHTML += template; let totalCost = Number.parseFloat( document.getElementById("total-cost").value ); document.getElementById("total-cost").value = totalCost + item.subTotal; } hoodie.store.withIdPrefix("item").on("add", addItemToPage);
في جزء التعليمات البرمجية هذا ، يتم الحصول على القالب من DOM ، ويتم استبدال العناصر النائبة ببيانات حقيقية ، ويتم إرفاق النتيجة بـ DOM. ثم يتم حساب مؤشر
total-cost
، وهو المبلغ الإجمالي لجميع عناصر القائمة ، والذي يتم عرضه أيضًا في الواجهة.
▍Vue
عند التبديل إلى Vue ، تم حذف القالب الذي يستخدمه البرنامج النصي ، وتمت إعادة تصميم عناصر الجدول بحيث تستخدم التوجيه Vue
v-for
، والذي يسمح لك بالتكرار خلال خاصية تحتوي على بيانات العناصر. هذه هي الطريقة التي يبدو بها الترميز المقابل الآن.
<div class="mdl-grid center-items"> <table id="item-table" class="mdl-data-table mdl-js-data-table mdl-shadow--2dp"> <thead> <tr> <th class="mdl-data-table__cell--non-numeric">Item Name</th> <th class="mdl-data-table__cell--non-numeric">Cost</th> <th class="mdl-data-table__cell--non-numeric">Quantity</th> <th class="mdl-data-table__cell">Sub-total</th> <th class="mdl-data-table__cell--non-numeric"> <button class="mdl-button mdl-js-button mdl-button--icon"> <i class="material-icons">delete</font></i> </button> </th> </tr> </thead> <tbody> <tr v-for="item in items" :key="item._id"> <td class="mdl-data-table__cell--non-numeric">{{ item.name}}</td> <td class="mdl-data-table__cell--non-numeric">{{ item.cost}}</td> <td class="mdl-data-table__cell--non-numeric">{{ item.quantity}}</td> <td class="mdl-data-table__cell">{{ item.subTotal}}</td> <td class="mdl-data-table__cell--non-numeric"> <button @click="deleteRow(item._id)" class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored"> <i class="material-icons">remove</font></i> </button> </td> </tr> </tbody> </table> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> <h4>Total Cost: {{ total }}</h4> </div> </div>
في الواقع ، لم يتم إجراء تغييرات كبيرة على الترميز. هنا ، يتم نسخ محتويات القالب الصغير المستخدم سابقًا ويتم استخدام التوجيهات وآليات الاستيفاء لنص Vue. يتم استخدام التوجيه
v-for
لسرد العناصر التي تم أخذ بياناتها من خاصية
items
. يتم عرض البيانات في أعمدة باستخدام إمكانات
{{ item.name }}
Vue
{{ item.name }}
. تشبه هذه الآلية العناصر النائبة المستخدمة في القالب الصغير. يتم عرض المبلغ الإجمالي أيضًا على الصفحة باستخدام تقنية الاستيفاء النصي.
الآن
index-vue.js
شفرة JavaScript في
index-vue.js
على ما يلي.
const vm = new Vue({ el: "#app", data: { name: "", cost: "", quantity: "", items: [] }, computed: { // total: function() { // `this` vm return this.items.reduce( (accumulator, currentValue) => accumulator + currentValue.subTotal, 0 ); } }, methods: { ..... } }); hoodie.store.withIdPrefix("item").on("add", item => vm.items.push(item));
تبين أن أحد أشكال الآلية الموصوفة ، التي أعدها Vue ، أقصر وأبسط بكثير مما تم إنشاؤه باستخدام JS العادي. في هذا الكود ، تمت إضافة خاصية بيانات
items
، والتي يتم استخدامها في
v-for
أعلاه للتوجيه. عند إضافة عنصر ، يستدعي Hoodie وظيفة تؤدي عملية
vm.items.push(item)
لتحديث حالة التطبيق ، وهذا بفضل نظام تفاعل Vue ، يقوم تلقائيًا بتحديث واجهة المستخدم. لحساب المبلغ الإجمالي ، ليست هناك حاجة للإشارة إلى عناصر DOM. هنا نستخدم خاصية محسوبة تعالج مجموعة بيانات
items
باستخدام
.reduce()
. الآن ، بفضل نظام Vue التفاعلية ، يتم تحديث واجهة المستخدم عندما تتغير هذه القيم. الشيء الجيد هو أن كل هذا يسمح للمبرمج بعدم القلق بشأن العمل مع عناصر DOM في التعليمات البرمجية. ونتيجة لذلك ، بمساعدة رمز أصغر ، حللنا نفس المشكلة ، والتي تتطلب المزيد من التعليمات البرمجية في حالة JS العادية (ربما ، إذا تم استخدام مكتبة jQuery بدلاً من JS العادية في هذه الحالة ، فإن القطة ستخرج أيضًا بحجم أكبر ، من تلك التي حصلت عليها عند استخدام Vue).
حفظ العناصر كقائمة
avaجافا سكريبت
بعد إضافة العناصر ، تحتاج إلى حفظها لاستخدامها لاحقًا ، بحيث يمكنك لاحقًا إضافة قوائم عناصر أخرى إلى النظام. يحتوي التطبيق على زر
Save List
، وهو المسؤول عن جمع البيانات ، لحفظها كمجموعة من العناصر باستخدام أدوات Hoodie ، ولتمكين المستخدم من إضافة مجموعة جديدة من العناصر إلى النظام.
عند استخدام JavaScript العادي ، ترتبط الوظيفة المقابلة
onClick
زر
onClick
. هنا الترميز.
//index.html <div class="mdl-grid center-items"> <button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onclick="pageEvents.saveList()"> Save List </button> </div>
هنا هو رمز الوظيفة.
//index.js function saveList() { let cost = 0; hoodie.store .withIdPrefix("item") .findAll() .then(function(items) { for (var item of items) { cost += item.subTotal; } // hoodie.store.withIdPrefix("list").add({ cost: cost, items: items }); // hoodie.store .withIdPrefix("item") .remove(items) .then(function() { // document.getElementById("item-table").tBodies[0].innerHTML = ""; // var snackbarContainer = document.querySelector("#toast"); snackbarContainer.MaterialSnackbar.showSnackbar({ message: "List saved succesfully" }); }) .catch(function(error) { // var snackbarContainer = document.querySelector("#toast"); snackbarContainer.MaterialSnackbar.showSnackbar({ message: error.message }); }); }); } window.pageEvents = { deleteItem: deleteItem, saveList: saveList .... };
▍Vue
لن يتطلب التبديل إلى Vue الكثير من التغيير. ما زلنا بحاجة إلى ربط المعالج بالحدث الذي يتم تشغيله عند النقر على الزر ، بالإضافة إلى ذلك ، نحتاج إلى إضافة الطريقة ، التي هي معالج هذا الحدث ، إلى خاصية
methods
الخاصة بكائن Vue عند تهيئته.
هذه هي الطريقة التي سيبدو بها الترميز الآن.
<div class="mdl-grid center-items"> <button @click="saveList" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored"> Save List </button> </div>
بناء
@click="saveList"
هو اختصار لـ
v-on:click="saveList"
. يتم استخدام هذه الآلية للاستماع إلى أحداث DOM. تمت إضافة نفس وظيفة
saveList
، التي تم استخدامها في نسخة التطبيق المكتوبة بلغة JS ، الى خاصية
methods
عنصر Vue.
شريط التنقل
avaجافا سكريبت
الآن بعد أن تم حفظ العناصر كقائمة ، نود أن نكون قادرين على عرض القوائم المحفوظة مسبقًا مع بيانات حول القيمة الإجمالية للسلع الموجودة فيها. سيتم عرض هذه المعلومات في صفحة أخرى ، ستبدو كما هو موضح أدناه.
بيانات قوائم المنتجات التي تم إنشاؤها مسبقًاترميز هذه الصفحة موجود في الملف
public/history.html
، ورمز JS المقابل موجود في الملف
public/js/src/history.js
. هذه الصفحة و
index.html
لديهما شيء مشترك. هذا هو شريط التنقل الموجود في أعلى النافذة. تحتوي هذه اللوحة على روابط لصفحات مختلفة وروابط
Register
الدخول والتسجيل التي تستدعي نماذج الحوار لدخول النظام والتسجيل فيه. يوجد أيضًا زر تسجيل الخروج لتسجيل الخروج.
في إصدار التطبيق الذي تم إنشاؤه باستخدام JS العادي ، كان عليك تكرار نفس الترميز على كلتا الصفحتين. هذا ما يبدو عليه رمز HTML لشريط التنقل.
<header class="mdl-layout__header"> <div class="mdl-layout__header-row"> <span class="mdl-layout-title">Shopping List</span> <div class="mdl-layout-spacer"></div> <nav class="mdl-navigation mdl-layout--large-screen-only"> <a class="mdl-navigation__link" href="index.html">Home</a> <a class="mdl-navigation__link" href="history.html">History</a> <a onclick="pageEvents.showLogin()" style="cursor: pointer" class="mdl-navigation__link login">Login</a> <a onclick="pageEvents.showRegister()" style="cursor: pointer" class="mdl-navigation__link register">Register</a> <a onclick="pageEvents.signout()" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a> </nav> </div> </header> <div class="mdl-layout__drawer"> <span class="mdl-layout-title">Shopping List</span> <nav class="mdl-navigation"> <a class="mdl-navigation__link" href="index.html">Home</a> <a class="mdl-navigation__link" href="history.html">History</a> <a onclick="pageEvents.showLogin()" style="cursor: pointer" class="mdl-navigation__link login">Login</a> <a onclick="pageEvents.showRegister()" style="cursor: pointer" class="mdl-navigation__link register">Register</a> <a onclick="pageEvents.signout()" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a> </nav> </div>
بعد تحليل هذا الرمز ، ستلاحظ أنه عند النقر على روابط
Logout
Login
Logout
وتسجيل
Logout
، سيتم استدعاء وظائفها المقابلة. يتم تعريف معالجات الأحداث للعناصر الموضحة في هذا الترميز في ملف
index.js
.
import * as shared from "shared.js"; .... shared.updateDOMLoginStatus(); window.pageEvents = { showLogin: shared.showLoginDialog, showRegister: shared.showRegisterDialog, signout: shared.signOut };
يتم الإعلان عن الوظائف المسؤولة عن العمل مع شريط التنقل في ملف
shared.js
.
// let loginDialog = document.querySelector("#login-dialog"); dialogPolyfill.registerDialog(loginDialog); let registerDialog = document.querySelector("#register-dialog"); dialogPolyfill.registerDialog(registerDialog); let showLoginDialog = function() { loginDialog.showModal(); }; let showRegisterDialog = function() { registerDialog.showModal(); }; let showAnonymous = function() { document.getElementsByClassName("login")[0].style.display = "inline"; document.getElementsByClassName("login")[1].style.display = "inline"; document.getElementsByClassName("register")[0].style.display = "inline"; document.getElementsByClassName("register")[1].style.display = "inline"; document.getElementsByClassName("logout")[0].style.display = "none"; document.getElementsByClassName("logout")[1].style.display = "none"; }; let showLoggedIn = function() { document.getElementsByClassName("login")[0].style.display = "none"; document.getElementsByClassName("login")[1].style.display = "none"; document.getElementsByClassName("register")[0].style.display = "none"; document.getElementsByClassName("register")[1].style.display = "none"; document.getElementsByClassName("logout")[0].style.display = "inline"; document.getElementsByClassName("logout")[1].style.display = "inline"; }; let updateDOMLoginStatus = () => { hoodie.account.get("session").then(function(session) { if (!session) { // showAnonymous(); } else if (session.invalid) { // , showAnonymous(); } else { // showLoggedIn(); } }); }; let signOut = function() { hoodie.account .signOut() .then(function() { showAnonymous(); let snackbarContainer = document.querySelector("#toast"); snackbarContainer.MaterialSnackbar.showSnackbar({ message: "You logged out" }); location.href = location.origin; }) .catch(function() { let snackbarContainer = document.querySelector("#toast"); snackbarContainer.MaterialSnackbar.showSnackbar({ message: "Could not logout" }); }); }; export { signOut, showRegisterDialog, showLoginDialog, updateDOMLoginStatus };
يصدر هذا الرمز الوظائف التي تم استخدامها في
index.js
. تعرض
showLoginDialog()
و
showRegisterDialog()
مربعات حوار
showRegisterDialog()
، على التوالي ، لتسجيل الدخول والتسجيل باستخدامها. تتيح وظيفة
showAnonymous()
للمستخدم تسجيل الخروج
showAnonymous()
وظيفة
showAnonymous()
، التي تخفي رابط
Logout
وتظهر روابط
Register
وتسجيل
Login
فقط.
updateDOMLoginStatus()
وظيفة
updateDOMLoginStatus()
إذا كان المستخدم
updateDOMLoginStatus()
ويعرض الارتباطات المناسبة. يتم استدعاء هذه الوظيفة عند تحميل الصفحة.
من أجل الحصول على شريط التنقل نفسه على صفحتين مختلفتين ، كان علينا تكرار الترميز ، والوصول إلى عناصر DOM واستخدام إمكانات CSS لإظهار الروابط وإخفائها على اللوحة. دعونا نلقي نظرة على شريط تنقل بديل تم إنشاؤه بواسطة Vue.
▍Vue
تحتوي العديد من تطبيقات الويب على نفس القصاصات المستخدمة في صفحات مختلفة. على سبيل المثال ، أشرطة التنقل. يجب وضع هذه الكيانات في حاويات أو تقديمها كمكونات مناسبة لإعادة الاستخدام. يوفر Vue للمطور محرك مكون يمكن استخدامه لحل مشاكل مثل تلك التي نفكر فيها. مكونات Vue مكتفية ذاتيا وقابلة لإعادة الاستخدام.
عند نقل المشروع لاستخدام مكونات Vue ، نقوم بإنشاء ملف جديد ،
shared-vue.js
. Vue, . .
Vue.component("navigation", { props: ["isLoggedIn", "toggleLoggedIn"], template: `<div> <header class="mdl-layout__header"> <div class="mdl-layout__header-row"> <span class="mdl-layout-title">Shopping List</span> <div class="mdl-layout-spacer"></div> <nav class="mdl-navigation mdl-layout--large-screen-only"> <a class="mdl-navigation__link" href="index.html">Home</a> <a class="mdl-navigation__link" href="history.html">History</a> <a v-show="!isLoggedIn" @click="showLogin" style="cursor: pointer" class="mdl-navigation__link login">Login</a> <a v-show="!isLoggedIn" @click="showRegister" style="cursor: pointer" class="mdl-navigation__link register">Register</a> <a v-show="isLoggedIn" @click="logout" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a> </nav> </div> </header> <div class="mdl-layout__drawer"> <span class="mdl-layout-title">Shopping List</span> <nav class="mdl-navigation"> <a class="mdl-navigation__link" href="index.html">Home</a> <a class="mdl-navigation__link" href="history.html">History</a> <a v-show="!isLoggedIn" @click="showLogin" style="cursor: pointer" class="mdl-navigation__link login">Login</a> <a v-show="!isLoggedIn" @click="showRegister" style="cursor: pointer" class="mdl-navigation__link register">Register</a> <a v-show="isLoggedIn" @click="logout" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a> </nav> </div> </div>`, methods: { showLogin: function() { const loginDialog = document.querySelector("#login-dialog"); dialogPolyfill.registerDialog(loginDialog); loginDialog.showModal(); }, showRegister: function() { const registerDialog = document.querySelector("#register-dialog"); dialogPolyfill.registerDialog(registerDialog); registerDialog.showModal(); }, logout: function() { hoodie.account .signOut() .then(() => { this.toggleLoggedIn(); }) .catch(error => { alert("Could not logout"); }); } } });
Vue,
navigation
, , , Vue. —
props .
props
— . , , ,
props
.
isLoggedIn
, .
,
template
, , . , , JS, , Vue —
v-show
@click
.
v-show
.
Logout
,
isLoggedIn
true
,
false
—
Login
Register
. , Vue
v-if
v-else
.
.
@click
—
v-on:click
.
showLogin
,
showRegister
logout
.
methods
.
logout
, ,
this.toggleLoggedIn()
,
props
. ,
props
, ,
isLoggedin
, . , Vue DOM.
index.html
. , 59-84. .
<navigation v-bind:is-logged-in="isLoggedIn" v-bind:toggle-logged-in="toggleLoggedIn"></navigation>
JS-
isLoggedIn
toggleLoggedIn
, ,
props
, , kebab-case.
v-bind
.
isLoggedIn
.
:
v-bind
, .
<navigation :is-logged-in="isLoggedIn" :toggle-logged-in="toggleLoggedIn"></navigation>
isLoggedIn
,
toggleLoggedIn
— , Vue
index-vue.js
. .
const vm = new Vue({ el: "#app", data: { name: "", cost: "", quantity: "", items: [], isLoggedIn: false }, computed: { .....// }, methods: { toggleLoggedIn: function() { this.isLoggedIn = !this.isLoggedIn; }, ......// } }); .....// hoodie.account.get("session").then(function(session) { if (!session) { // vm.isLoggedIn = false; } else if (session.invalid) { vm.isLoggedIn = false; } else { // vm.isLoggedIn = true; } });
Vue , , , , Vue. , Vue DOM , .
▍JavaScript
Login
Register
, . , , , . 171-244
index.html
100-158
history.html
.
<dialog id="login-dialog" class="mdl-dialog"> <h4 class="mdl-dialog__title">Login</h4> <div class="mdl-dialog__content"> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <input class="mdl-textfield__input" type="text" id="login-username"> <label class="mdl-textfield__label" for="login-username">Username</label> </div> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <input class="mdl-textfield__input" type="password" id="login-password"> <label class="mdl-textfield__label" for="login-password">Password</label> </div> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <span id="login-error"></span> </div> </div> </div> <div class="mdl-dialog__actions"> <button onclick="pageEvents.closeLogin()" type="button" class="mdl-button close">Cancel</button> <button onclick="pageEvents.login()" type="button" class="mdl-button">Login</button> </div> </dialog> <dialog id="register-dialog" class="mdl-dialog"> <h4 class="mdl-dialog__title">Login</h4> <div class="mdl-dialog__content"> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <input class="mdl-textfield__input" type="text" id="register-username"> <label class="mdl-textfield__label" for="register-username">Username</label> </div> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <input class="mdl-textfield__input" type="password" id="register-password"> <label class="mdl-textfield__label" for="register-password">Password</label> </div> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <span id="register-error"></span> </div> </div> </div> <div class="mdl-dialog__actions"> <button onclick="pageEvents.closeRegister()" type="button" class="mdl-button close">Cancel</button> <button onclick="pageEvents.register()" type="button" class="mdl-button">Register</button> </div> </dialog>
, ,
shared.js
index.js
.
//shared.js // let loginDialog = document.querySelector("#login-dialog"); dialogPolyfill.registerDialog(loginDialog); let registerDialog = document.querySelector("#register-dialog"); dialogPolyfill.registerDialog(registerDialog); let closeLoginDialog = function() { loginDialog.close(); }; let closeRegisterDialog = function() { registerDialog.close(); }; let showAnonymous = function() { ... }; let showLoggedIn = function() { .... }; let signOut = function() { .... }; let updateDOMLoginStatus = () => { .... }; let login = function() { let username = document.querySelector("#login-username").value; let password = document.querySelector("#login-password").value; hoodie.account .signIn({ username: username, password: password }) .then(function() { showLoggedIn(); closeLoginDialog(); let snackbarContainer = document.querySelector("#toast"); snackbarContainer.MaterialSnackbar.showSnackbar({ message: "You logged in" }); }) .catch(function(error) { console.log(error); document.querySelector("#login-error").innerHTML = error.message; }); }; let register = function() { let username = document.querySelector("#register-username").value; let password = document.querySelector("#register-password").value; let options = { username: username, password: password }; hoodie.account .signUp(options) .then(function(account) { return hoodie.account.signIn(options); }) .then(account => { showLoggedIn(); closeRegisterDialog(); return account; }) .catch(function(error) { console.log(error); document.querySelector("#register-error").innerHTML = error.message; }); }; export { register, login, closeRegisterDialog, closeLoginDialog, ... };
index.js.
//index.js window.pageEvents = { closeLogin: shared.closeLoginDialog, showLogin: shared.showLoginDialog, closeRegister: shared.closeRegisterDialog, showRegister: shared.showRegisterDialog, login: shared.login, register: shared.register, signout: shared.signOut };
▍Vue
Vue . , .
Vue.component("login-dialog", { data: function() { return { username: "", password: "" }; }, props: ["toggleLoggedIn"], template: `<dialog id="login-dialog" class="mdl-dialog"> <h4 class="mdl-dialog__title">Login</h4> <div class="mdl-dialog__content"> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <input v-model="username" class="mdl-textfield__input" type="text" id="login-username"> <label class="mdl-textfield__label" for="login-username">Username</label> </div> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <input v-model="password" class="mdl-textfield__input" type="password" id="login-password"> <label class="mdl-textfield__label" for="login-password">Password</label> </div> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield"> <span id="login-error"></span> </div> </div> </div> <div class="mdl-dialog__actions"> <button @click="closeLogin" type="button" class="mdl-button close">Cancel</button> <button @click="login" type="button" class="mdl-button">Login</button> </div> </dialog>`, methods: { closeLogin: function() { const loginDialog = document.querySelector("#login-dialog"); dialogPolyfill.registerDialog(loginDialog); loginDialog.close(); }, login: function(event) { hoodie.account .signIn({ username: this.username, password: this.password }) .then(() => { this.toggleLoggedIn(); this.closeLogin(); }) .catch(error => { console.log(error); document.querySelector("#login-error").innerHTML = "Error loggin in"; }); } } });
data
,
props
,
template
methods
,
Vue.component()
.
, , Vue .
//index.html <login-dialog v-bind:toggle-logged-in="toggleLoggedIn"></login-dialog>
.
, , Vue. ,
, . ,
- Push API .
الملخص
-, JS, Vue.js. HTML, CSS JavaScript (jQuery), Vue . ES6 , .
Vue , , . , , , Vue. , , Vue .
, JS.
— , Vue.
أعزائي القراء! - ?
