テーマまたはプラグインからのプラグインアウトレットコネクタの使用

Discourse には数百のプラグインアウトレットが含まれており、これらを使用して新しいコンテンツを挿入したり、Discourse UI の既存のコンテンツを置き換えたりできます。コンテキストに基づいてコンテンツをカスタマイズできるように、「アウトレット引数」が利用可能になっています。

アウトレットの選択

プラグインアウトレットの名前を見つけるには、Discourse コア内で “<PluginOutlet” を検索するか、プラグインアウトレットの場所テーマコンポーネントを使用します。(例: topic-above-posts)。

ラッパーアウトレット

コア内のいくつかのアウトレットは <PluginOutlet @name="foo" /> のように見えます。これらは新しいコンテンツを挿入できるようにします。他のいくつかのアウトレットは、次のように既存のコア実装を「ラップ」します。

<PluginOutlet @name="foo">
  core implementation
</PluginOutlet>

この種の「ラッパー」アウトレットのコネクタを定義すると、コア実装が置き換えられます。ラッパープラグインアウトレットに対してコネクタを提供するアクティブなテーマ/プラグインは 1 つだけです。

ラッパープラグインアウトレットの場合、{{yield}} キーワードを使用して元のコア実装をレンダリングできます。これは、特定の条件下でのみコア実装を置き換えたい場合や、何らかの要素でラップしたい場合に役立ちます。

テンプレートの定義

アウトレットを選択したら、コネクタの名前を決定します。これは、インストールされているすべてのテーマ/プラグイン全体で一意である必要があります。例: brand-official-topics

テーマ/プラグイン内で、次のような形式のパスを持つ新しいハンドルバーテンプレートを定義します。

:art: {theme}/javascripts/discourse/connectors/{outlet-name}/{connector-name}.hbs

:electric_plug: {plugin}/assets/javascripts/discourse/connectors/{outlet-name}/{connector-name}.hbs

これらのファイルの内容は Ember コンポーネントとしてレンダリングされます。Ember / Handlebars に関する一般的な情報については、Ember ガイドを参照してください。

架空の「ブランド公式トピック」コネクタの場合、テンプレートは次のようになります。

<div class="alert alert-info">
  This topic was created by a member of the
  <a href="https://discourse.org/team">Discourse Team</a>
</div>

一部のプラグインアウトレットは、コンテンツを HTML 要素で自動的にラップします。要素のタイプは、<PluginOutlet>@connectorTagName によって定義されます。

アウトレット引数の使用

プラグインアウトレットは、@outletArgs 経由で周囲のコンテキストに関する情報を提供します。各アウトレットに渡される引数は異なります。引数を確認する簡単な方法は、テンプレートにこれを追加することです。

{{log @outletArgs}}

これにより、ブラウザの開発者コンソールに引数がログ出力されます。これらは Proxy オブジェクトとして表示されます。引数のリストを調べるには、プロキシの [[Target]] を展開します。

topic-above-posts の例では、レンダリングされているトピックは @outletArgs.model で利用可能です。したがって、チームメンバーのユーザー名を次のように追加できます。

<div class="alert alert-info">
  This topic was created by
  {{@outletArgs.model.details.created_by.username}}
  (a member of the
  <a href="https://discourse.org/team">Discourse Team</a>)
</div>

より複雑なロジックの追加

単純なハンドルバーテンプレートでは不十分な場合があります。コネクタに Javascript ロジックを追加するには、ハンドルバーテンプレートに隣接する Javascript ファイルを定義できます。このファイルはコンポーネント定義をエクスポートする必要があります。これは、サービスインジェクションを含む、他のコンポーネント定義とまったく同じように機能します。

このようにコンポーネントを定義すると、自動的な connectorTagName ラッパー要素が削除されるため、hbs ファイルで同じタイプの要素を再導入したい場合があります。

topic-above-posts の例では、「UX でユーザー名を優先する」サイト設定に基づいてユーザーを異なる方法でレンダリングしたい場合があります。そのコンポーネント定義は次のようになるかもしれません。

.../connectors/topic-above-posts/brand-official-topic.js:

import Component from "@glimmer/component";
import { service } from "@ember/service";

export default class BrandOfficialTopics extends Component {
  @service siteSettings;

  get displayName() {
    const user = this.args.outletArgs.model.details.created_by;
    if (this.siteSettings.prioritize_username_in_ux) {
      return user.username;
    } else {
      return user.name;
    }
  }
}

次に、新しいゲッターを参照するようにテンプレートを更新できます。

<div class="alert alert-info">
  This topic was created by
  {{this.displayName}}
  (a member of the
  <a href="https://discourse.org/team">Discourse Team</a>)
</div>

条件付きレンダリング

コンテンツを特定の条件下でのみレンダリングしたい場合は、テンプレートをハンドルバーの {{#if}} ブロックでラップするだけで十分なことがよくあります。それが不十分な場合は、コネクタテンプレートがレンダリングされるかどうかを制御するために shouldRender フックを使用したい場合があります。

まず、上記で説明したように .js コネクタ定義があることを確認します。次に、静的 static shouldRender() 関数を追加します。例を拡張します。

import Component from "@glimmer/component";
import { getOwner } from "discourse-common/lib/get-owner";

export default class BrandOfficialTopics extends Component {
  static shouldRender(outletArgs, helper) {
    const firstPost = outletArgs.model.postStream.posts[0];
    return firstPost.primary_group_name === "team";
  }
  // ... (その他のロジック)
}

これで、トピックの最初の投稿がチームメンバーによって作成された場合にのみ、コネクタがレンダリングされます。

shouldRender は Glimmer の自動追跡コンテキストで評価されます。参照されたプロパティ(例: outletArgs)の将来の変更により、関数が再評価されます。

新しいアウトレットの導入

まだ存在しないアウトレットが必要な場合は、お気軽にプルリクエストを作成するか、Dev でトピックを開いてください。


このドキュメントはバージョン管理されています - 変更の提案は github で行えます。

「いいね!」 39
Using discourse's plugin outlets
What is the best way to integrate member applications?
Add HTML (Link) Next To Logo
Group Semantics
Can I put the search form at the top of our 404 page?
How to show user total post count beside name
Native theme support
Developing Discourse Plugins - Part 2 - Connect to a plugin outlet
Feedback on "on-discourse" javascript for setting up custom JS for each page?
Topic-timeline api.decorateWidget call has stopped working
Developing Discourse Plugins - Part 2 - Connect to a plugin outlet
Developing Discourse Themes & Theme Components
How to add btn before "sign in"
Minimizing Maintenance on Theme Customizations
How to add custom fields to models
Tags at the top of the topic list in a Category
I want to insert images (banner) between the topic answers. How do I start?
How to add a link shortcut to the area under the title
Baidu Search
Add Banner/HTML (Widget) before reply button
Upcoming Header Changes - Preparing Themes and Plugins
Upgrading Discourse to Ember 4
Converting modals from legacy controllers to new DModal component API
Need help integrating code wrote on Edittext to the Discourse
Settings not appearing
How to add a custom button in user profile card?
How to add a custom button in user profile card?
Customizing the topic list
(not recommended) Overriding Discourse templates from a Theme or Plugin
How to override the site-header.hbs file from custom theme?
Upcoming topic-list changes - how to prepare themes and plugins
Working with .erb templates in a plugin
How to Integrate a Custom Plugin in discourse UI
Modernizing inline script tags for templates & JS API
Custom Components -- add button or text at any plugin outlet
(not recommended) Overriding Discourse templates from a Theme or Plugin
Display Tags inline with thread title, instead of being on the bottom line
Add link to external SSO profile to profile page
Discourse view file update does not reflect in browser
Discourse view file update does not reflect in browser
Add likes and views to search display
Using template hbs to add HTML content to a plugin outlet
Adding a billing section in the member section
Adding a billing section in the member section
How to modify the header HTML, but still remaining the default founctions
Removing support for "template overrides" and mobile-specific templates
How can i add image in login and register box
Most “traditional” or classic forum Category listing
Newbie help accessing code
How to add custom html next to logo using discourse plugin methods
Using the DModal API to render Modal windows (aka popups/dialogs) in Discourse
Air Theme
How to create a plugin with backend API calls to populate composer while drafting?
How to add a custom url text link on the login page
Add Text In Header Beside Logo
Split up theme Javascript into multiple files

これは非推奨になったということですよね?
非推奨通知: registerConnectorClass を介したコネクタクラスの定義は非推奨です。よりモダンなパターンについては https://meta.discourse.org/t/32727 を参照してください。[deprecation id: discourse.register-connector-class-legacy]

はい、代わりにapi.renderInOutletを使用できます。:slight_smile:

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.js#L982-L1008

「いいね!」 1

それよりももう少し複雑だと思います :sweat_smile:
https://github.com/Firepup6500/discourse-custom-profile-link/blob/master/common/head_tag.html

心配しないでください。後でできる限りのことをします(今は寝る必要があります)。:smile:

「いいね!」 1

ありがとうございます! (コードがひどい状態ですみません、前回触ったときは動くだけでよかったんです :sweat_smile:)

「いいね!」 1

HEAD ファイル内のコンポーネントに関する通知を受け取りましたが、api.renderInOutlet を使用して書き直す方法がわかりません。

  const ajax = require('discourse/lib/ajax').ajax;
  const Topic = require('discourse/models/topic').default;
  // Discourse の ajax と Topic モデルを使用しています

  api.registerConnectorClass('above-main-container', 'featured-topics', {
    // above-main-container はプラグインのアウトレットです
    // featured-topics はカスタムコンポーネント名です

    setupComponent(args, component) {

   // コードの残りは続きます

api.registerConnectorClassapi.renderInOutlet に置き換えようとしましたが、うまくいきませんでした。テーマのコーディングはあまり得意ではありません。助けていただけると幸いです。

こちらで例を確認できます。

StatBannercomponents ディレクトリで定義されたネイティブクラスです。

この場合、api.renderInOutlet("above-main-container", YourClass) となります。

HEAD ファイルではできないと思います。コードを複数のファイルに分割する必要があります。

開発を容易にするために、Discourse Theme CLI の使用をお勧めします。これにより、Theme コンポーネントの開発がはるかに簡単になります。

Theme コンポーネントは公開されていますか?

「いいね!」 3

.js ファイルで現在のコンセントを取得する方法はありますか?

それは不可能だと思います。

以前は、クラシックコンポーネントで parentView を検査することができました。

しかし、そのプロパティは非推奨になりました。

「いいね!」 1

「現在のコンセントを取得する」とはどういう意味ですか?コンセントの名前が必要ですか?それとも何か他のものですか?

問題の解決策を見つけました(こちら)、それは次のようなものでした。

  • 3つの異なるアウトレット用の .hbs ファイルと .js ファイルを作成します。
  • 各 JS ファイルで、使用しているアウトレットが設定 banner_location の値であるかどうかを確認します。
  • もしそうなら、バナーを表示します。そうでなければ、バナーを非表示にします。
「いいね!」 1

クール!ダイナミックなアウトレット名を持つ api.renderInOutlet を使用することにしたようですね :chefs_kiss:

「いいね!」 1