フッターのロードごと(またはページのロードごと)にファイアする方法

フッターに 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>Sign Up for Our Blog</h5>
</div>

ページが最初に読み込まれるときは正常に動作します。しかし、2 回目以降のページ(少なくとも大部分)ではフォームの読み込みに失敗し、以下のエラーが表示されます:

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 を特定する必要があるのでしょう。

こちらに少し情報が載っています。

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

以下を使用する場合、

api.onAppEvent("inserted-custom-html", () => {
  // some code
});

どのカスタム HTML をターゲットにするかを指定する必要があります。それに応じて異なるイベントが発火します。

この場合、フッターをターゲットにしたいようです。これを試して、動作するか確認してもらえますか?

api.onAppEvent("inserted-custom-html:footer", () => {
  // some code
});

ハハ!それだ!(もちろん、彼らがフッターでやろうとしていたことがそもそもやる価値のないことだと私が説得した「後」の話ですが)。これで解決できて本当に嬉しいです。ご協力ありがとうございました。少しずつですが、この手のことが理解できるようになってきました。(そして今や、彼らはフッター自体を削除してしまうかもしれません。)

ああ!「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 は、私にとって最も理解するのが難しいものです。1980 年代半ばに Lisp を学んで以来、何が「自明」なのかを判断するのがまだ難しいのです。

正確には違います。そのイベントは、custom-html Ember コンポーネントがレンダリングされたときのみ発火します。

では、その Ember コンポーネントはどこで使用され、何を行うのでしょうか?

主に、メインの application template で、2 つのテーマフィールドをレンダリングするために使用します。

テーマの after_header タブ

テーマのフッタータブ

他の場所でも使用されていますが、この文脈では重要ではありません。

フッターには triggerAppEvent="true" も設定されていることに注意してください。

そのため、以下のように使用できます。

api.onAppEvent("inserted-custom-html:footer", () => {
  // いくつかのコード
});

after_header タブでは同じことはできません。Discourse はそのためのイベントを発火させないためです。したがって、以下は機能しません。

api.onAppEvent("inserted-custom-html:top", () => {
  // いくつかのコード
});

では、なぜその Ember コンポーネントを使ってテーマのフィールドをレンダリングするのでしょうか?

簡単な答えは、レンダリングのタイミングをより細かく制御できるからです。

その例を2つ挙げます… トピックリストと管理ページです。

無限スクロールでまだトピックが読み込まれている間は、トピックリストにフッターをレンダリングしたくありません。

また、after_header タブで追加されたバナーやサイトブランディングのマークアップを管理ページでレンダリングしたくありません。

したがって、custom-html Ember コンポーネントを使用することで、そのような点についてよりきめ細やかな制御が可能になります。

さて、あなたの質問に戻ります。フッターがレンダリングされたときに何らかの処理を行う必要がある場合、必要なラッパーは以下の通りです。

api.onAppEvent("inserted-custom-html:footer", () => {
  // いくつかのコード
});

その API メソッドは、すべてのアプリイベントで機能します。そのため、そこにある例が完全ではないとしても、それは単なる例に過ぎません。そのメソッドで使用できる他の AppEvents はたくさんあります。例えば、こちらを確認してください。

発火するイベントのいくつかの名前を確認できます。

this.appEvents.trigger("EVENT_NAME")

そして、他の変更を加えるためにそれらをフックする方法

this.appEvents.on("EVENT_NAME")

Discourse コアが発火する任意のイベントをテーマでフックできます。つまり、コンポーザーが開く直前に何らかのコードを発火させたい場合、

テーマに以下のようなものを追加できます。

api.onAppEvent("composer:will-open", () => {
  console.log("これはコンポーザーが開く直前に発火します");
});

とは言え、私はあなたに同意します。API ファイル内のそのメソッドについては、別の例を使用すべきです。その例を改善するようメモしました。

これは素晴らしい回答で、非常に明確なので、私でも理解できました。

「あるものを発火させる方法」という問題はよく話題に上がります。もしかしたらここで見つける人もいるかもしれませんが、最後の投稿だけでも独立したトピックとして作成し、テーマやプラグインのガイドに追加またはリンクする価値があるかもしれません。

また、現在「テーマ」は主に「Ember」を、「プラグイン」は主に「Rails」を指すようになっているため、それらを少しリファクタリングすることを検討してもよいかもしれません。当初、いくつかの Ember 機能にはプラグインが必要でしたが、現在はテーマコンポーネントで実現できるものがありますよね。そして現在、Ember 関連の処理はプラグインまたはテーマコンポーネントのどちらでも行えます。

@Johani さん、こんにちは。「トリガーの発火方法」に関するこの問題の別バージョンとして、私がプラグイン内で Custom Header Links のバージョンを使用しているという状況があります。これは、私のプラグインが追加した別モデルで作成されたアイテム(「サーバー」)へのリンクを張るものです。server が作成された際、ヘッダーリンクを再構築して、最も最近作成された 2 つのサーバーへのリンクを張りたいと考えています。現在はイニシャライザー内でこれを行っており、機能は問題ないのですが、サーバー追加後に更新するにはページを再読み込みする必要があります。

以前、トリガーの追加と監視方法について教えていただいたので、自分で解決できるかと思いましたが、その処理を行っているページは discourse-subscriptions にあります。そこで、購入完了後に discourse-subscriptions に対して PR を提出し、以下のようなコードを追加するのはどうでしょうか。

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

購入が完了した後(購入がグループへの追加をトリガーし、それがサーバーの作成とユーザーのグループからの除外をトリガーする)に実行されるようにします。あるいは、購入完了後、または「購入完了」モーダルで「OK」をクリックした後にページを再読み込みさせるだけでも構いませんが、その方法もわかりません(試した方法はページが無限に再読み込みされてしまいました…)。

そこで、おそらくここ:

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

で、ローディングフラグを false に設定した後に以下を追加したいと考えています。

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

そうすれば、私のイニシャライザーで以下を追加してヘッダーの操作を行えるようになります。

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

そうすれば、おそらく以下のようなコードを変更する必要があります。なぜなら、このコードは header-buttons:before に追加するだけで置換しないため、発火するたびにリンクが増え続けてしまうのではないかと心配だからです。

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

これでまた助かりました!投稿ストリームが更新されたときにスクリプトを読み込む必要がありました。見つけたすべての例は、this を介して appEvent にアクセスできるコンポーネントにありました。ついにこれを見つけ、これで apiInitializer

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

そして、投稿の各バッチが読み込まれるときに、それらの広告が読み込まれるようになりました。すでにあなたの投稿に :heart: を付けたので、もっと丁寧にお礼を書かなければなりませんでした。 :wink:

これは私のお気に入りのトピックの1つです:heart:時々ここに戻ってきます:sweat_smile:本当に感謝しています!:hugs: