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

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

アウトレットの選択

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

ラッパーアウトレット

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

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

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

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

コネクタの定義

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

テーマ/プラグレットで、次のような形式のパスを持つ新しい .gjs コネクタを定義します。

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

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

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

私たちの仮想的な「brand official topics」コネクタの場合、ファイルは次のようになります。

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

アウトレット引数の使用

プラグレットアウトレットは、@outletArgs を介して周囲の文脈に関する情報を提供します。各アウトレットに渡される引数は異なります。引数を表示する簡単な方法は、テンプレートにこれを追加することです。

{{log @outletArgs}}

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

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

<template>
  <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>
</template>

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

時折、単純なテンプレートでは不十分な場合があります。コネクタに JavaScript ロジックを追加するには、.gjs ファイルをクラスベースのコンポーネントをエクスポートするようにアップグレードします。これは他のコンポーネント定義と同じように機能し、サービス注入を含めることができます。

topic-above-posts の例では、「UX でユーザー名を優先する」サイト設定に基づいてユーザーを異なる方法でレンダリングしたい場合があります。.gjs ファイルは次のようになります。

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

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;
    }
  }

  <template>
    <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>
  </template>
}

条件付きレンダリング

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

まず、上記の説明に従ってクラスベースの .gjs コネクタを持っていることを確認します。次に、static shouldRender() 関数を追加します。例を拡張します。

import Component from "@glimmer/component";

export default class BrandOfficialTopics extends Component {
  static shouldRender(outletArgs, helper) {
    const firstPost = outletArgs.model.postStream.posts[0];
    return firstPost.primary_group_name === "team";
  }
  // ... (any other logic)

  <template>
    {{! ... }}
  </template>
}

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

shouldRender は Glimmer の自動追跡コンテキストで評価されます。参照されているプロパティ(例:outletArgs)への将来の変更は、関数が再評価される原因となります。

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

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


このドキュメントはバージョン管理されています。変更提案は 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
Developing Discourse Plugins - Part 2 - Connect to a plugin outlet
Native theme support
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
Add link to external SSO profile to profile page
How to add a custom button in user profile card?
(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
Templating of my "component" broke. How do I fix it?
Templating of my "component" broke. How do I fix it?
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
Adding "latest topics" header in the main interface
Display Tags inline with thread title, instead of being on the bottom line
How to add a custom button in user profile card?
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