تطوير التطبيقات باستخدام إطار تطوير التطبيقات السريع AllcountJS
نشرت: 2022-03-11ولدت فكرة التطوير السريع للتطبيق (RAD) استجابة لنماذج تطوير الشلال التقليدية. توجد العديد من الاختلافات في RAD. على سبيل المثال ، التطوير السريع Agile and the Rational Unified Process. ومع ذلك ، فإن كل هذه النماذج تشترك في شيء واحد: فهي تهدف إلى تحقيق أقصى قيمة للأعمال مع الحد الأدنى من وقت التطوير من خلال النماذج الأولية والتطوير التكراري. لتحقيق ذلك ، يعتمد نموذج تطوير التطبيقات السريعة على الأدوات التي تسهل العملية. في هذه المقالة ، سنستكشف إحدى هذه الأدوات ، وكيف يمكن استخدامها للتركيز على قيمة الأعمال وتحسين عملية التطوير.
AllcountJS هو إطار عمل ناشئ مفتوح المصدر تم إنشاؤه مع وضع التطوير السريع للتطبيقات في الاعتبار. يعتمد على فكرة تطوير التطبيق التعريفي باستخدام كود تكوين يشبه JSON يصف هيكل وسلوك التطبيق. تم بناء إطار العمل على Node.js و Express و MongoDB ويعتمد بشكل كبير على AngularJS و Twitter Bootstrap. على الرغم من أنه يعتمد على الأنماط التعريفية ، إلا أن إطار العمل لا يزال يسمح بمزيد من التخصيص من خلال الوصول المباشر إلى API عند الحاجة.
لماذا AllcountJS كإطار عمل RAD الخاص بك؟
وفقًا لـ Wikipedia ، هناك ما لا يقل عن مائة أداة تعد بالتطوير السريع للتطبيقات ، ولكن هذا يثير السؤال: ما مدى "السرعة". هل تسمح هذه الأدوات بتطوير تطبيق معين يركز على البيانات في غضون ساعات قليلة؟ أو ربما يكون الأمر "سريعًا" إذا كان من الممكن تطوير التطبيق في غضون أيام قليلة أو بضعة أسابيع. حتى أن بعض هذه الأدوات تدعي أن بضع دقائق هي كل ما يتطلبه الأمر لإنتاج تطبيق عملي. ومع ذلك ، فمن غير المحتمل أن تتمكن من إنشاء تطبيق مفيد في أقل من خمس دقائق وما زلت تدعي أنك قد استوفت كل احتياجات العمل. لا تدعي AllcountJS أنها مثل هذه الأداة ؛ ما يقدمه AllcountJS هو طريقة لعمل نموذج أولي لفكرة في فترة زمنية قصيرة.
باستخدام إطار عمل AllcountJS ، من الممكن إنشاء تطبيق بواجهة مستخدم قابلة للتكوين تلقائيًا ، وميزات إدارة المستخدم ، وواجهة برمجة تطبيقات RESTful وعدد قليل من الميزات الأخرى بأقل جهد ووقت. من الممكن استخدام AllcountJS لمجموعة متنوعة من حالات الاستخدام ، لكنه يناسب التطبيقات التي يكون لديك فيها مجموعات مختلفة من الكائنات ذات طرق عرض مختلفة لها. عادةً ما تكون تطبيقات الأعمال مناسبة جدًا لهذا النموذج.
تم استخدام AllcountJS لبناء موقع allcountjs.com ، بالإضافة إلى أداة تعقب المشروع الخاصة به. من الجدير بالذكر أن allcountjs.com هو تطبيق AllcountJS مخصص ، وأن AllcountJS يسمح بدمج كل من العروض الثابتة والديناميكية مع القليل من المتاعب. حتى أنه يسمح بإدراج الأجزاء المحملة ديناميكيًا في محتوى ثابت. على سبيل المثال ، يدير AllcountJS مجموعة من قوالب التطبيق التجريبي. يوجد عنصر واجهة مستخدم تجريبي على الصفحة الرئيسية لـ allcountjs.com يقوم بتحميل قالب تطبيق عشوائي من تلك المجموعة. تتوفر مجموعة من نماذج التطبيقات الأخرى في المعرض على allcountjs.com.
ابدء
لإثبات بعض إمكانيات إطار عمل RAD AllcountJS ، سننشئ تطبيقًا بسيطًا لـ Toptal ، والذي سنطلق عليه Toptal Community. إذا كنت تتابع مدونتنا ، فقد تعلم بالفعل أن تطبيقًا مشابهًا قد تم إنشاؤه باستخدام Hoodie كجزء من إحدى منشوراتنا السابقة في المدونة. سيسمح هذا التطبيق لأعضاء المجتمع بالتسجيل وإنشاء الأحداث والتقدم لحضورها.
لإعداد البيئة ، يجب عليك تثبيت Node.js و MongoDB و Git. بعد ذلك ، قم بتثبيت AllcountJS CLI عن طريق استدعاء أمر "تثبيت npm" وتنفيذ الأمر init:
npm install -g allcountjs-cli allcountjs init toptal-community-allcount cd toptal-community-allcount npm install
سيطلب منك AllcountJS CLI إدخال بعض المعلومات حول مشروعك لملء package.json مسبقًا.
يمكن استخدام AllcountJS كخادم مستقل أو تبعية. في مثالنا الأول ، لن نقوم بتوسيع AllcountJS ، لذلك يجب أن يعمل الخادم المستقل فقط من أجلنا.
داخل دليل app-config الذي تم إنشاؤه حديثًا ، سنقوم باستبدال محتويات ملف JavaScript main.js بالمقتطف التالي من التعليمات البرمجية:
A.app({ appName: "Toptal Community", onlyAuthenticated: true, allowSignUp: true, appIcon: "rocket", menuItems: [{ name: "Events", entityTypeId: "Event", icon: "calendar" }, { name: "My Events", entityTypeId: "MyEvent", icon: "calendar" }], entities: function(Fields) { return { Event: { title: "Events", fields: { eventName: Fields.text("Event").required(), date: Fields.date("Date").required(), time: Fields.text("Starts at").masked("99:99").required(), appliedUsers: Fields.relation("Applied users", "AppliedUser", "event") }, referenceName: "eventName", sorting: [['date', -1], ['time', -1]], actions: [{ id: "apply", name: "Apply", actionTarget: 'single-item', perform: function (User, Actions, Crud) { return Crud.actionContextCrud().readEntity(Actions.selectedEntityId()).then(function (eventToApply) { var userEventCrud = Crud.crudForEntityType('UserEvent'); return userEventCrud.find({filtering: {"user": User.id, "event": eventToApply.id}}).then(function (events) { if (events.length) { return Actions.modalResult("Can't apply to event", "You've already applied to this event"); } else { return userEventCrud.createEntity({ user: {id: User.id}, event: {id: eventToApply.id}, date: eventToApply.date, time: eventToApply.time }).then(function () { return Actions.navigateToEntityTypeResult("MyEvent") }); } }); }) } }] }, UserEvent: { fields: { user: Fields.fixedReference("User", "OnlyNameUser").required(), event: Fields.fixedReference("Event", "Event").required(), date: Fields.date("Date").required(), time: Fields.text("Starts at").masked("99:99").required() }, filtering: function (User) { return {"user.id": User.id} }, sorting: [['date', -1], ['time', -1]], views: { MyEvent: { title: "My Events", showInGrid: ['event', 'date', 'time'], permissions: { write: [], delete: null } }, AppliedUser: { permissions: { write: [] }, showInGrid: ['user'] } } }, User: { views: { OnlyNameUser: { permissions: { read: null, write: ['admin'] } }, fields: { username: Fields.text("User name") } } } } } });
على الرغم من أن AllcountJS يعمل مع مستودعات Git ، إلا أننا لن نستخدمه في هذا البرنامج التعليمي من أجل البساطة. لتشغيل تطبيق Toptal Community ، كل ما يتعين علينا القيام به هو استدعاء أمر تشغيل AllcountJS CLI في دليل toptal-community-allcount.
allcountjs run
تجدر الإشارة إلى أنه يجب تشغيل MongoDB عند تنفيذ هذا الأمر. إذا سارت الأمور على ما يرام ، يجب أن يكون التطبيق قيد التشغيل على http: // localhost: 9080.
لتسجيل الدخول يرجى استخدام اسم المستخدم "admin" وكلمة المرور "admin".
أقل من 100 سطر
ربما لاحظت أن التطبيق المحدد في main.js أخذ 91 سطرًا فقط من التعليمات البرمجية. تتضمن هذه الأسطر إعلانًا عن جميع السلوكيات التي قد تلاحظها عند الانتقال إلى http: // localhost: 9080. إذن ، ما الذي يحدث بالضبط تحت الغطاء؟ دعونا نلقي نظرة فاحصة على كل جانب من جوانب التطبيق ، ونرى كيف ترتبط الكود بها.
تسجيل الدخول والاشتراك
الصفحة الأولى التي تراها بعد فتح التطبيق هي تسجيل دخول. يتضاعف هذا كصفحة تسجيل ، على افتراض أن مربع الاختيار - المسمى "تسجيل" - قد تم فحصه قبل إرسال النموذج.
تظهر هذه الصفحة لأن ملف main.js يصرح بأن المستخدمين المصادق عليهم فقط هم من يمكنهم استخدام هذا التطبيق. علاوة على ذلك ، فإنه يتيح للمستخدمين إمكانية التسجيل من هذه الصفحة. السطران التاليان هما كل ما كان ضروريًا لهذا:
A.app({ ..., onlyAuthenticated: true, allowSignUp: true, ... })
صفحة الترحيب
بعد تسجيل الدخول ، ستتم إعادة توجيهك إلى صفحة ترحيب بها قائمة تطبيق. يتم إنشاء هذا الجزء من التطبيق تلقائيًا ، بناءً على عناصر القائمة المحددة تحت مفتاح "menuItems".
إلى جانب اثنين من التكوينات الأخرى ذات الصلة ، يتم تحديد القائمة في ملف main.js على النحو التالي:
A.app({ ..., appName: "Toptal Community", appIcon: "rocket", menuItems: [{ name: "Events", entityTypeId: "Event", icon: "calendar" }, { name: "My Events", entityTypeId: "MyEvent", icon: "calendar" }], ... });
يستخدم AllcountJS أيقونات Font Awesome ، لذلك يتم تعيين جميع أسماء الرموز المشار إليها في التكوين إلى أسماء أيقونات Font Awesome.
تصفح وتحرير الأحداث
بعد النقر على "الأحداث" من القائمة ، سيتم نقلك إلى عرض الأحداث الموضح في لقطة الشاشة أدناه. إنها طريقة عرض AllcountJS القياسية التي توفر بعض وظائف CRUD العامة في الكيانات المقابلة. هنا ، يمكنك البحث عن الأحداث وإنشاء أحداث جديدة وتعديل أو حذف الأحداث الموجودة. يوجد وضعان لواجهة CRUD هذه: القائمة والشكل. يتم تكوين هذا الجزء من التطبيق من خلال الأسطر القليلة التالية من كود JavaScript.
A.app({ ..., entities: function(Fields) { return { Event: { title: "Events", fields: { eventName: Fields.text("Event").required(), date: Fields.date("Date").required(), time: Fields.text("Starts at").masked("99:99").required(), appliedUsers: Fields.relation("Applied users", "AppliedUser", "event") }, referenceName: "eventName", sorting: [['date', -1], ['time', -1]], ... } } } });
يوضح هذا المثال كيفية تكوين أوصاف الكيانات في AllcountJS. لاحظ كيف نستخدم وظيفة لتحديد الكيانات ؛ يمكن أن تكون كل خاصية من خصائص تكوين AllcountJS دالة. يمكن أن تطلب هذه الوظائف التبعيات ليتم حلها من خلال أسماء الوسيطات الخاصة بها. قبل استدعاء الوظيفة ، يتم إدخال التبعيات المناسبة. هنا ، تعد "الحقول" واحدة من واجهات برمجة تطبيقات تكوين AllcountJS المستخدمة لوصف حقول الكيان. تحتوي الخاصية "الكيانات" على أزواج من الاسم والقيمة حيث يكون الاسم معرّف نوع الكيان والقيمة هي وصفها. يتم وصف نوع الكيان للأحداث ، في هذا المثال ، حيث العنوان هو "الأحداث". يمكن هنا أيضًا تحديد التكوينات الأخرى ، مثل ترتيب الفرز الافتراضي واسم المرجع وما شابه. يتم تحديد الترتيب الافتراضي للفرز من خلال مجموعة من أسماء الحقول والاتجاهات ، بينما يتم تحديد اسم المرجع من خلال سلسلة (اقرأ المزيد هنا).
تم تعريف نوع الكيان هذا على أنه يحتوي على أربعة حقول: "اسم الحدث" و "التاريخ" و "الوقت" و "المستخدمين المطبقين" ، والتي تظل الثلاثة الأولى منها موجودة في قاعدة البيانات. هذه الحقول إلزامية ، كما يتضح من استخدام "مطلوب ()." يتم التحقق من صحة القيم الموجودة في هذه الحقول مع هذه القواعد قبل إرسال النموذج في الواجهة الأمامية كما هو موضح في لقطة الشاشة أدناه. يجمع AllcountJS بين عمليات التحقق من صحة العميل والخادم لتوفير أفضل تجربة للمستخدم. الحقل الرابع عبارة عن علاقة تحتوي على قائمة بالمستخدمين الذين تقدموا بطلبات لحضور الحدث. بطبيعة الحال ، لا يتم الاحتفاظ بهذا الحقل في قاعدة البيانات ، ويتم ملؤه عن طريق تحديد كيانات AppliedUser ذات الصلة بالحدث فقط.
التقدم لحضور الأحداث
عندما يختار المستخدم حدثًا معينًا ، يعرض شريط الأدوات زرًا يسمى "تطبيق". يؤدي النقر فوقه إلى إضافة الحدث إلى جدول المستخدم. في AllcountJS ، يمكن تكوين إجراءات مشابهة لهذا ببساطة عن طريق التصريح عنها في التكوين:

actions: [{ id: "apply", name: "Apply", actionTarget: 'single-item', perform: function (User, Actions, Crud) { return Crud.actionContextCrud().readEntity(Actions.selectedEntityId()).then(function (eventToApply) { var userEventCrud = Crud.crudForEntityType('UserEvent'); return userEventCrud.find({filtering: {"user": User.id, "event": eventToApply.id}}).then(function (events) { if (events.length) { return Actions.modalResult("Can't apply to event", "You've already applied to this event"); } else { return userEventCrud.createEntity({ user: {id: User.id}, event: {id: eventToApply.id}, date: eventToApply.date, time: eventToApply.time }).then(function () { return Actions.navigateToEntityTypeResult("MyEvent") }); } }); }) } }]
تأخذ خاصية "الإجراءات" لأي نوع كيان مجموعة من الكائنات التي تصف سلوك كل إجراء مخصص. يحتوي كل كائن على خاصية "id" التي تحدد معرفًا فريدًا للإجراء ، وتحدد الخاصية "name" اسم العرض ويتم استخدام الخاصية "actionTarget" لتحديد سياق الإجراء. يشير تعيين "actionTarget" إلى "single-item" إلى أنه يجب تنفيذ الإجراء مع حدث معين. الوظيفة المحددة ضمن الخاصية "Perform" هي المنطق المنفذ عند تنفيذ هذا الإجراء ، عادةً عندما ينقر المستخدم على الزر المقابل.
قد يتم طلب التبعيات بواسطة هذه الوظيفة. على سبيل المثال ، في هذا المثال ، تعتمد الوظيفة على "المستخدم" و "الإجراءات" و "الخام". عند حدوث إجراء ، يمكن الحصول على إشارة إلى المستخدم ، الذي يستدعي هذا الإجراء ، من خلال طلب تبعية "المستخدم". التبعية "Crud" ، والتي تسمح بمعالجة حالة قاعدة البيانات لهذه الكيانات ، مطلوبة هنا أيضًا. الطريقتان اللتان تعيدان مثيل لكائن Crud هما: الطريقة "actionContextCrud ()" - تُرجع CRUD لنوع الكيان "Event" لأن الإجراء "Apply" ينتمي إليها ، بينما الطريقة "crudForEntityType ()" - تُرجع CRUD لأي نوع كيان محدد بواسطة معرف النوع الخاص به.
يبدأ تنفيذ الإجراء بالتحقق مما إذا كان هذا الحدث قد تمت جدولته بالفعل للمستخدم ، وإذا لم يكن كذلك ، فإنه يقوم بإنشاء حدث. إذا تمت جدولته بالفعل ، فسيتم عرض مربع حوار عن طريق إرجاع القيمة من استدعاء "Actions.modalResult ()". إلى جانب إظهار الحالة ، قد يؤدي الإجراء أنواعًا مختلفة من العمليات بطريقة مماثلة ، مثل "التنقل للعرض" ، و "عرض التحديث" ، و "إظهار الحوار" ، وما إلى ذلك.
جدول المستخدم للأحداث التطبيقية
بعد التقديم بنجاح على حدث ما ، تتم إعادة توجيه المتصفح إلى عرض "أحداثي" ، والذي يعرض قائمة بالأحداث التي قام المستخدم بتطبيقها. يتم تحديد العرض من خلال التكوين التالي:
UserEvent: { fields: { user: Fields.fixedReference("User", "OnlyNameUser").required(), event: Fields.fixedReference("Event", "Event").required(), date: Fields.date("Date").required(), time: Fields.text("Starts at").masked("99:99").required() }, filtering: function (User) { return {"user.id": User.id} }, sorting: [['date', -1], ['time', -1]], views: { MyEvent: { title: "My Events", showInGrid: ['event', 'date', 'time'], permissions: { write: [], delete: null } }, AppliedUser: { permissions: { write: [] }, showInGrid: ['user'] } } },
في هذه الحالة ، نستخدم خاصية تكوين جديدة ، "التصفية". كما في المثال السابق ، تعتمد هذه الوظيفة أيضًا على تبعية "المستخدم". إذا قامت الدالة بإرجاع كائن ، فسيتم التعامل معها كاستعلام MongoDB ؛ يقوم الاستعلام بتصفية المجموعة للأحداث التي تخص المستخدم الحالي فقط.
خاصية أخرى مثيرة للاهتمام هي "المناظر". "العرض" هو نوع كيان عادي ، ولكن مجموعة MongoDB هي نفسها بالنسبة لنوع الكيان الأصلي. وهذا يجعل من الممكن إنشاء طرق عرض مختلفة بصريًا لنفس البيانات في قاعدة البيانات. في الواقع ، لقد استخدمنا هذه الميزة لإنشاء طريقتين مختلفتين لعرض "UserEvent": "MyEvent" و "AppliedUser". نظرًا لأنه تم تعيين النموذج الأولي للعروض الفرعية على نوع الكيان الأصلي ، فإن الخصائص التي لم يتم تجاوزها يتم "اكتسابها" من النوع الرئيسي.
سرد الحاضرين الحدث
بعد التقديم على حدث ، قد يرى المستخدمون الآخرون قائمة بجميع المستخدمين الذين يخططون للحضور. يتم إنشاء هذا كنتيجة لعناصر التكوين التالية في main.js:
AppliedUser: { permissions: { write: [] }, showInGrid: ['user'] } // ... appliedUsers: Fields.relation("Applied users", "AppliedUser", "event")
"AppliedUser" هي طريقة عرض للقراءة فقط لنوع كيان "MyEvent". يتم فرض إذن القراءة فقط هذا عن طريق تعيين مصفوفة فارغة إلى خاصية "الكتابة" الخاصة بكائن الأذونات. أيضًا ، نظرًا لعدم تحديد إذن "القراءة" ، يُسمح بالقراءة افتراضيًا لجميع المستخدمين.
تمديد التطبيقات الافتراضية
السحب النموذجي لأطر عمل RAD هو الافتقار إلى المرونة. بمجرد إنشاء تطبيقك وتحتاج إلى تخصيصه ، قد تواجه عقبات كبيرة. تم تطوير AllcountJS مع مراعاة القابلية للتوسعة ويسمح باستبدال كل لبنة أساسية بالداخل.
لتحقيق ذلك ، يستخدم AllcountJS تطبيق حقن التبعية (DI) الخاص به. يسمح DI للمطور بتجاوز السلوكيات الافتراضية لإطار العمل من خلال نقاط الامتداد ، وفي نفس الوقت ، يسمح له من خلال إعادة استخدام التطبيقات الحالية. تم وصف العديد من جوانب تمديد إطار عمل RAD في الوثائق. في هذا القسم ، سوف نستكشف كيف يمكننا توسيع مكونين من العديد من المكونات في إطار العمل ، ومنطق جانب الخادم وطرق العرض.
بالاستمرار في مثال Toptal Community ، دعنا ندمج مصدر بيانات خارجي لتجميع بيانات الحدث. لنتخيل أن هناك منشورات مدونة Toptal تناقش خطط الأحداث في اليوم السابق لكل حدث. باستخدام Node.js ، يجب أن يكون من الممكن تحليل موجز RSS للمدونة واستخراج هذه البيانات. للقيام بذلك ، سنحتاج إلى بعض تبعيات npm الإضافية ، مثل "request" و "xml2js" (لتحميل موجز RSS لمدونة Toptal) و "q" (لتنفيذ الوعود) و "لحظة" (لتحليل التواريخ). يمكن تثبيت هذه التبعيات عن طريق استدعاء مجموعة الأوامر التالية:
npm install xml2js npm install request npm install q npm install moment
دعنا ننشئ ملف JavaScript آخر ، نسميه "toptal-community.js" في دليل toptal-community-allcount ونملأه بما يلي:
var request = require('request'); var Q = require('q'); var xml2js = require('xml2js'); var moment = require('moment'); var injection = require('allcountjs'); injection.bindFactory('port', 9080); injection.bindFactory('dbUrl', 'mongodb://localhost:27017/toptal-community'); injection.bindFactory('gitRepoUrl', 'app-config'); injection.bindFactory('DiscussionEventsImport', function (Crud) { return { importEvents: function () { return Q.nfcall(request, "https://www.toptal.com/blog.rss").then(function (responseAndBody) { var body = responseAndBody[1]; return Q.nfcall(xml2js.parseString, body).then (function (feed) { var events = feed.rss.channel[0].item.map(function (item) { return { eventName: "Discussion of " + item.title, date: moment(item.pubDate, "DD MMM YYYY").add(1, 'day').toDate(), time: "12:00" }}); var crud = Crud.crudForEntityType('Event'); return Q.all(events.map(function (event) { return crud.find({query: {eventName: event.eventName}}).then(function (createdEvent) { if (!createdEvent[0]) { return crud.createEntity(event); } }); } )); }); }) } }; }); var server = injection.inject('allcountServerStartup'); server.startup(function (errors) { if (errors) { throw new Error(errors.join('\n')); } });
في هذا الملف ، نحدد تبعية تسمى "DiscussionEventsImport" ، والتي يمكننا استخدامها في ملف main.js عن طريق إضافة إجراء استيراد على نوع الكيان "Event".
{ id: "import-blog-events", name: "Import Blog Events", actionTarget: "all-items", perform: function (DiscussionEventsImport, Actions) { return DiscussionEventsImport.importEvents().then(function () { return Actions.refreshResult() }); } }
نظرًا لأنه من المهم إعادة تشغيل الخادم بعد إجراء بعض التغييرات على ملفات JavaScript ، يمكنك إنهاء المثيل السابق وتشغيله مرة أخرى عن طريق تنفيذ الأمر نفسه كما كان من قبل:
node toptal-community.js
إذا سارت الأمور على ما يرام ، فسترى شيئًا مثل لقطة الشاشة أدناه بعد تشغيل إجراء "استيراد أحداث المدونة".
جيد حتى الآن ، ولكن دعونا لا نتوقف هنا. تعمل طرق العرض الافتراضية ، لكنها قد تكون مملة في بعض الأحيان. دعونا نخصصهم قليلا.
هل تحب الكروت؟ الجميع يحب البطاقات! لعرض البطاقة ، ضع ما يلي في ملف يسمى events.jade داخل دليل app-config:
extends main include mixins block vars - var hasToolbar = true block content .refresh-form-controller(ng-app='allcount', ng-controller='EntityViewController') +defaultToolbar() .container.screen-container(ng-cloak) +defaultList() .row: .col-lg-4.col-md-6.col-xs-12(ng-repeat="item in items") .panel.panel-default .panel-heading h3 {{item.date | date}} {{item.time}} div button.btn.btn-default.btn-xs(ng-if="!isInEditMode", lc-tooltip="View", ng-click="navigate(item.id)"): i.glyphicon.glyphicon-chevron-right | button.btn.btn-danger.btn-xs(ng-if="isInEditMode", lc-tooltip="Delete", ng-click="deleteEntity(item)"): i.glyphicon.glyphicon-trash .panel-body h3 {{item.eventName}} +noEntries() +defaultEditAndCreateForms() block js +entityJs()
بعد ذلك ، قم بالإشارة إليه ببساطة من كيان "الحدث" في main.js على أنه "عرض مخصص:" أحداث ". قم بتشغيل التطبيق الخاص بك وسترى واجهة قائمة على البطاقة بدلاً من الواجهة الافتراضية الجدولية.
خاتمة
في الوقت الحاضر ، يتشابه تدفق تطوير تطبيقات الويب عبر العديد من تقنيات الويب ، حيث تتكرر بعض العمليات مرارًا وتكرارًا. هل حقا يستحق ذلك؟ ربما حان الوقت لإعادة التفكير في طريقة تطوير تطبيقات الويب الخاصة بك؟
يوفر AllcountJS نهجًا بديلاً لأطر تطوير التطبيقات السريعة ؛ تبدأ بإنشاء هيكل للتطبيق من خلال تحديد أوصاف الكيان ، ثم إضافة طرق العرض وتخصيصات السلوك حوله. كما ترون ، باستخدام AllcountJS ، أنشأنا تطبيقًا بسيطًا ، لكنه يعمل بكامل طاقته ، في أقل من مائة سطر من التعليمات البرمجية. ربما لا يلبي جميع متطلبات الإنتاج ، لكنه قابل للتخصيص. كل هذا يجعل AllcountJS أداة جيدة لتمهيد تطبيقات الويب بسرعة.