أضف مكونات Ember إلى Discourse

في البرنامج التعليمي السابق أوضحت كيفية تكوين كل من جانب الخادم وجانب العميل في Discourse للاستجابة لطلب ما.

نوصيك الآن بقراءة وثائق مكون Ember: Introducing Components - Components - Ember Guides

البرنامج التعليمي القديم

في هذا البرنامج التعليمي، سأقوم بإنشاء مكون Ember جديد كوسيلة لتغليف جافاسكريبت (Javascript) تابعة لجهة خارجية. سيكون هذا مشابهًا لفيديو يوتيوب قمت بإنشائه منذ فترة، والذي قد تجده مفيدًا، ولكن هذه المرة هو خاص بـ Discourse وكيفية ترتيب الملفات في مشروعنا.

لماذا المكونات؟

Handlebars هي لغة قوالب بسيطة جدًا. إنها مجرد HTML عادي إلى جانب بعض الأجزاء الديناميكية. هذا سهل التعلم ورائع للإنتاجية، ولكنه ليس رائعًا لإعادة استخدام التعليمات البرمجية. إذا كنت تقوم بتطوير تطبيق كبير مثل Discourse، فستجد أنك تريد إعادة استخدام بعض الأشياء نفسها مرارًا وتكرارًا.

المكونات هي حل Ember لهذه المشكلة. لنقم بإنشاء مكون سيعرض الوجبة الخفيفة (snack) بطريقة أجمل.

إنشاء مكون جديد

يجب أن تحتوي المكونات على شرطة في أسمائها. سأختار fancy-snack كاسم لهذا المكون. لنقم بإنشاء القالب الخاص بنا:

app/assets/javascripts/admin/templates/components/fancy-snack.hbs

<div class="fancy-snack-title">
  <h1>{{snack.name}}</h1>
</div>

<div class="fancy-snack-description">
  <p>{{snack.description}}</p>
</div>

الآن، لاستخدام المكون الخاص بنا، سنقوم باستبدال قالب admin/snack الحالي بما يلي:

app/assets/javascripts/admin/templates/snack.hbs

{{fancy-snack snack=model}}

يمكننا الآن إعادة استخدام المكون fancy-snack الخاص بنا في أي قالب آخر، فقط بتمرير النموذج (model) حسب الحاجة.

إضافة كود جافاسكريبت مخصص

بالإضافة إلى إمكانية إعادة الاستخدام، تعد المكونات في Ember رائعة لإضافة جافاسكريبت مخصص و jQuery وأكواد خارجية أخرى بأمان. يمنحك هذا تحكمًا في متى يتم إدراج المكون في الصفحة، ومتى تتم إزالته. للقيام بذلك، نقوم بتعريف Ember.Component مع بعض التعليمات البرمجية:

app/assets/javascripts/admin/components/fancy-snack.js

export default Ember.Component.extend({
  didInsertElement() {
    this._super();
    this.$().animate({ backgroundColor: "yellow" }, 2000);
  },

  willDestroyElement() {
    this._super();
    this.$().stop();
  },
});

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

دعنا نشرح ما يحدث هنا:

  1. عندما يتم عرض المكون على الصفحة، سيتم استدعاء didInsertElement

  2. السطر الأول من didInsertElementwillDestroyElement) هو this._super() وهو ضروري لأننا نورث من Ember.Component.

  3. يتم إجراء الحركة باستخدام دالة jQuery’s animate.

  4. أخيرًا، يتم إلغاء الحركة في خطاف willDestroyElement، والذي يتم استدعاؤه عند إزالة المكون من الصفحة.

قد تتساءل لماذا نهتم بـ willDestroyElement على الإطلاق؛ السبب هو أنه في تطبيق جافاسكريبت طويل الأمد مثل Discourse، من المهم تنظيف ما بعدنا، حتى لا نتسرب الذاكرة أو نترك الأشياء قيد التشغيل. في هذه الحالة، نوقف الحركة، مما يخبر أي مؤقتات jQuery بأنه ليس من الضروري أن تظهر بعد الآن لأن المكون لم يعد مرئيًا على الصفحة.

الخطوات التالية

البرنامج التعليمي النهائي في هذه السلسلة يغطي الاختبار الآلي.


يتم التحكم في إصدار هذه الوثيقة - اقترح تغييرات على github.

17 إعجابًا

Hi, how do i extend a discourse component thru a plugin? Can you give me some points. Thanks

Generally we prefer you don’t extend Discourse plugins, and you stick to plugin outlets or using the widget decoration API to add stuff.

But if you must, you can create an initializer and use Ember’s extend code. Here’s an example that extends an Ember object.

4 إعجابات

Tried with initializer but didnt worked. What i actually want to do is to add 2 more classNames and some actions:

import { withPluginApi } from 'discourse/lib/plugin-api';

function initializeComponentTopicList(api) {
  // extend component from jsapp/components/topic-list.js.es6
  const TopicList = api.container.lookupFactory('component:topic-list');

  TopicList.extend({
    classNames: ['topic-list', 'round', 'table'],
    actions: {
        clickMe: function() {
            console.log('click');
        }
    }
  });
};

export default {
  name: "extend-for-component-topic-list",

  initialize() {
    withPluginApi('0.1', initializeComponentTopicList);
  }
};

And by “didnt worked”, i mean that topic list completly disappeared from page.
Thank you

Were there any logs in the console?

Nope, no logs at all. However i managed to fix it this way. I hope it will help someone.

import { default as TopicList } from 'discourse/components/topic-list';
import { withPluginApi } from 'discourse/lib/plugin-api';

function initializeComponentTopicList(api) {
  TopicList.reopen({
    classNames: ['topic-list', 'round', 'table'],
  });
};

export default {
  name: "extend-for-component-topic-list",

  initialize() {
    withPluginApi('0.1', initializeComponentTopicList);
  }
};
3 إعجابات

I just ran into the same problem. Add the following as a css/html customisation and observe empty user cards:

<script type="text/discourse-plugin" version="0.5">
    api.container.lookupFactory('component:user-card-contents')
</script>
إعجابَين (2)

سيكون من الجيد رؤية وثائق محدثة هنا، والتي يمكن أن تشير إلى وثائق مكون Glimmer.

3 إعجابات