كيفية إطلاق النار على كل تحميل تذييل (أو تحميل صفحة؟)

أحاول تحميل نموذج JavaScript في التذييل.

لدي هذا في الرأس:

<script type="text/discourse-plugin" version="0.8">
  let loadScript = require("discourse/lib/load-script").default;

  api.onPageChange(() => {
    loadScript("//js.hsforms.net/forms/current.js").then(() => {
      console.log("doing the thing");
      hbspt.forms.create({
        portalId: "229276",
        formId: "a86ca9cc",
        submitButtonClass: "button orange-button hubspot-button",
        target: ".subscription-form"
      });
    });
  });
</script>

وهذا في التذييل:

<div class="subscription-form clearfix">
  <h5>اشترك في مدونتنا</h5>
</div>

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

Couldn't find target container .subscription-form for HubSpot Form a86ca9cc. Not rendering form onto the page

أعتقد أن التذييل ربما يتم تحميله بعد تنفيذ السكربت؟

لذلك أحتاج بطريقة ما إلى تأخير تنفيذ السكربت حتى يتم تحميل التذييل.

يُظهر ملف plugin-api.js ما يلي:

// Listen for a triggered `AppEvent` from Discourse.

api.onAppEvent("inserted-custom-html", () => {
  console.log("a custom footer was rendered");
});

ربما أحتاج فقط إلى معرفة أي AppEvent يجب البحث عنه؟

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

هناك بعض المعلومات حول هذا الأمر هنا.

Javascripts targeting the footer doesn't work after page transitions - #4 by Johani

عند استخدامك لما يلي

api.onAppEvent("inserted-custom-html", () => {
  // بعض الأكواد
});

يجب عليك تحديد نوع HTML المخصص الذي تريد استهدافه، حيث تُفعّل أحداث مختلفة بناءً على ذلك.

في هذه الحالة، تريد استهداف التذييل. هل يمكنك تجربة هذا والتحقق مما إذا كان يعمل معك؟

api.onAppEvent("inserted-custom-html:footer", () => {
  // بعض الأكواد
});
إعجاب واحد (1)

ها! لقد نجح الأمر! (بالطبع، كان ذلك بعد أن أقنعتهم بأن ما كانوا يحاولون فعله في التذييل لا يستحق القيام به من الأساس). أنا متحمس تمامًا لأنني تمكنت من حل هذه المشكلة وأقدر مساعدتك؛ فأنا أتقدم ببطء نحو فهم هذه الأمور. (وأعتقد أنهم الآن سيقومون بحذف التذييل بالكامل.)

آها! لست متأكدًا من السبب في أنني لم أفكر في البحث عن “footer”! :man_shrugging:

إذن، يشير inserted-custom-html إلى أي شيء في السمات؟ ثم يمكنني إضافة :footer أو :header أو ربما :head_tag؟ هل هذه هي السحر؟ هل أنا الوحيد، أم أن

https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/app/lib/plugin-api.js#L516

كان سيكون أكثر فائدة لو كان مكتوبًا بدلاً من ذلك كالتالي:

   api.onAppEvent('inserted-custom-html:footer', () => {

يُعد JavaScript و Ember أصعب الأشياء بالنسبة لي لفهمها منذ… ربما عندما تعلمت Lisp في منتصف الثمانينيات، لذا لا زلت لا أستطيع تحديد ما يُعتبر “واضحًا” بدقة.

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

ليس تمامًا. يحدث هذا الحدث فقط عند عرض مكون custom-html ember.

إذن، أين نستخدم مكون ember هذا، وماذا يفعل؟

نستخدمه بشكل رئيسي في قالب التطبيق الرئيسي لعرض حقلين من سمات التصميم.

علامة التبويب after_header في السمة

علامة التبويب التذييل (footer) في السمة

لاحظ أننا نستخدمه في أماكن أخرى، لكنها لا تهم في هذا السياق.

لاحظ كيف يحتوي التذييل أيضًا على triggerAppEvent="true".

ولهذا السبب يمكنك استخدام:

api.onAppEvent("inserted-custom-html:footer", () => {
  // بعض الأكواد
});

لا يمكنك فعل الشيء نفسه مع علامة التبويب after_header. لا يقوم Discourse بإطلاق حدث لذلك. لذا، لن يعمل هذا.

api.onAppEvent("inserted-custom-html:top", () => {
  // بعض الأكواد
});

الآن، لماذا نستخدم مكون ember هذا لعرض بعض حقول السمة؟

الإجابة البسيطة هي أنه يمنحنا تحكمًا أكبر بكثير في توقيت عرضه.

مثالان على ذلك… قائمة المواضيع وصفحة المسؤول.

لا نريد عرض التذييل في قائمة المواضيع بينما لا تزال هناك مواضيع سيتم تحميلها عبر التمرير اللانهائي.

كما لا نريد عرض أي لافتات أو علامات تجارية للموقع في صفحة المسؤول المضافة في علامة التبويب after_header.

لذا، يمنحنا استخدام مكون custom-html ember تحكمًا أدق في أشياء من هذا القبيل.

الآن، نعود إلى سؤالك. إذا كان هناك بعض العمل الذي تحتاج إلى القيام به عند عرض التذييل، فهذا هو الغلاف الذي تحتاجه:

api.onAppEvent("inserted-custom-html:footer", () => {
  // بعض الأكواد
});

تعمل طريقة واجهة برمجة التطبيقات (API) هذه لجميع أحداث التطبيق. لذا، على الرغم من أن المثال الموجود فيها غير مكتمل حقًا، فهو مجرد مثال. هناك العديد من AppEvents الأخرى التي يمكنك استخدامها في هذه الطريقة. على سبيل المثال، يمكنك التحقق من هنا:

لمشاهدة أسماء بعض الأحداث التي يتم إطلاقها.

this.appEvents.trigger("EVENT_NAME")

وكيفية ربطنا بها لإجراء تغييرات أخرى:

this.appEvents.on("EVENT_NAME")

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

discourse/app/assets/javascripts/discourse/app/components/composer-editor.js at 1c38b4abf1fab8d67aaaa4b9f2810add64b709c4 · discourse/discourse · GitHub.

يمكنني إضافة شيء مثل هذا إلى سمة التصميم الخاصة بي:

api.onAppEvent("composer:will-open", () => {
  console.log("هذا يتم تشغيله مباشرة قبل فتح محرر الرد");
});

مع ذلك، أنا أتفق معك. يجب أن نستخدم مثالًا مختلفًا لتلك الطريقة في ملف واجهة برمجة التطبيقات (API). لقد قمت بتدوين ملاحظة لتحسين هذا المثال.

إعجابَين (2)

هذا رد رائع وواضح جدًا لدرجة أنني تمكنت من فهمه حتى أنا.

تتكرر مشكلة “كيفية جعل شيء ما يُفعّل” بشكل متكرر. ربما يجد الناس إجاباتهم هنا، لكن منشورك الأخير بحد ذاته يمكن أن يكون موضوعًا مستقلًا، وربما يُضاف أو يُربط به في أدلة السمات و/أو الإضافات.

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

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

مرحبًا @Johani، إذن هناك إصدار آخر من هذه المشكلة المتعلقة بـ “كيف يتم تشغيل أو كيف يتم تحفيز”، وهو أنني أستخدم نسخة من Custom Header Links في إضافة. تقوم هذه الإضافة بربط بعض العناصر (“الخوادم”) التي تم إنشاؤها في نموذج منفصل تضيفه إضافتي. عند إنشاء خادم، أريد إعادة بناء روابط الرأس لربطها بأحدث خادمين تم إنشاؤهما. أقوم بذلك حاليًا في مُهيئ (initializer) ويعمل بشكل جيد، إلا أنه لتحديث الروابط بعد إضافة خادم، يجب إعادة تحميل الصفحة.

لقد أخبرتني كيفية إضافة ومراقبة المحفزات (triggers)، فظننت أنني قد أستطيع حل هذه المشكلة، لكن الصفحة التي تقوم بالعمل موجودة في discourse-subscriptions. ربما ما أريده هو تقديم طلب دمج (PR) إلى discourse-subscriptions يضيف:

 this.appEvents.trigger("purchase-complete")

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

لذا ربما هنا:

https://github.com/discourse/discourse-subscriptions/blob/main/assets/javascripts/discourse/controllers/s-show.js.es6#L71-L81

أريد إضافة:

 this.appEvents.trigger("successful-transaction")

بعد تعيين المتغير loading إلى false، ثم يمكنني أن أضيف في مُهيئتي:

 this.appEvents.on("successful-transaction")

لإجراء التعديل على الروابط؟

أعتقد أنه بمجرد القيام بذلك، سأحتاج إلى فعل شيء مختلف لأنني أخشى أن:

      api.decorateWidget("header-buttons:before", (helper) => {
        return helper.h("ul.pfaffmanager-header-links", headerLinks);
      });

تضيف إلى header-buttons:before بدلاً من استبدالها، وبالتالي سأحصل على روابط إضافية في كل مرة يتم فيها استدعاؤها؟

لقد ساعدني هذا مرة أخرى! ما احتجت إلى فعله هو تحميل نص برمجي عند تحديث تدفق المنشورات. كانت جميع الأمثلة التي وجدتها في مكونات كان لديها وصول إلى appEvent من خلال this. لقد وجدت هذا أخيرًا والآن في apiInitializer يمكنني

  api.onAppEvent('post-stream:refresh', args => {
   // do some stuff!!!
  });

والآن يتم تحميل تلك الإعلانات عند تحميل كل دفعة من المنشورات. لقد قمت بالفعل بالإعجاب بمنشورك، لذلك كان عليّ أن أكتب شكرًا أكثر تفصيلاً. :wink:

إعجابَين (2)

هذا أحد مواضيعي المفضلة :heart: أعود إلى هنا من وقت لآخر :sweat_smile: أقدر هذا حقًا! :hugs:

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