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

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

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

الدرس القديم

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

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

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

المكونات هي حل Ember لهذه المشكلة. دعنا ننشئ مكونًا سيعرض وجبتنا الخفيفة بطريقة أجمل.

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

يجب أن تحتوي المكونات على شرطة في اسمها. سأختار 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 في أي قالب آخر، مع تمرير النموذج حسب الحاجة.

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

إلى جانب إعادة الاستخدام، تُعد مكونات 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();
  },
});

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

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

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

  2. السطر الأول من didInsertElementwillDestroyElement) هو this._super() وهو ضروري لأننا نقوم بـ تصنيف فرعي لـ Ember.Component.

  3. يتم تنفيذ الحركة باستخدام دالة jQuery’s animate.

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

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

إلى أين نتجه بعد ذلك

يتناول الدرس النهائي في هذه السلسلة الاختبار الآلي.


يتم التحكم في إصدارات هذا المستند - اقترح التغييرات على GitHub.

18 إعجابًا

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

إعجاب واحد (1)

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 إعجابات