从主题或插件使用插件出口连接器

Discourse 包含数百个插件出口(Plugin Outlets),可用于在 Discourse UI 中注入新内容或替换现有内容。“出口参数”(Outlet arguments)可用于根据上下文自定义内容。

选择出口

要查找插件出口的名称,请在 Discourse 核心代码中搜索 “<PluginOutlet”,或使用 插件出口位置 主题组件。(例如 topic-above-posts)。

包装器出口

核心中的一些出口看起来像 <PluginOutlet @name="foo" />。这些出口允许您注入新内容。其他出口则会以如下方式“包装”现有的核心实现:

<PluginOutlet @name="foo">
  核心实现
</PluginOutlet>

为这种“包装器”出口定义连接器将替换核心实现。对于包装器插件出口,同一社区中只能有一个活跃的主题或插件贡献连接器。

对于包装器插件出口,您可以使用 {{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 指南

对于我们要假设的“品牌官方主题”连接器,文件内容可能如下所示:

<template>
  <div class="alert alert-info">
    此主题由
    <a href="https://discourse.org/team">Discourse 团队</a>
    的成员创建
  </div>
</template>

使用出口参数

插件出口通过 @outletArgs 提供有关周围上下文的信息。传递给每个出口的参数各不相同。查看参数的简单方法是在模板中添加以下内容:

{{log @outletArgs}}

这将把参数记录到浏览器的开发者控制台中。它们将显示为 Proxy 对象——要查看参数列表,请展开代理的 [[Target]]

在我们的 topic-above-posts 示例中,渲染的主题可在 @outletArgs.model 下找到。因此,我们可以这样添加团队成员的用户名:

<template>
  <div class="alert alert-info">
    此主题由
    {{@outletArgs.model.details.created_by.username}}
    (
    <a href="https://discourse.org/team">Discourse 团队</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.displayName}}
      (
      <a href="https://discourse.org/team">Discourse 团队</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";
  }
  // ... (其他逻辑)

  <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 重写。\n\n\n\n const ajax = require('discourse/lib/ajax').ajax;\n const Topic = require('discourse/models/topic').default;\n // 我们正在使用 Discourse 的 ajax 和 Topic 模型\n\n api.registerConnectorClass('above-main-container', 'featured-topics', {\n // above-main-container 是插件出口,\n // featured-topics 是你的自定义组件名称\n\n setupComponent(args, component) {\n\n // 剩余代码如下\n\n我猜我尝试用 api.renderInOutlet 替换 api.registerConnectorClass,但它出错了。我在这方面不是主题编码专家。感谢任何帮助。

您可以在此处看到一个示例:

StatBanner 是在 components 目录中定义的本地类:

在您的情况下,那将是 api.renderInOutlet("above-main-container", YourClass)

我不认为您可以在 HEAD 文件中执行此操作。您应该将代码拆分成多个文件

我鼓励您使用Discourse Theme CLI,因为它将使开发 Theme component 更加容易!

您的 Theme Component 是公开的吗?

3 个赞

我的 .js 文件中有什么方法可以获取当前的 outlet 吗?

我认为那是不可能的。

以前在经典组件中可以检查 parentView

但是该属性已被弃用。

1 个赞

您说的“获取当前插座”是什么意思?您想要插座的名称吗?还是其他什么?

我在我的问题 (这里) 中找到了一个解决方案,它大致是这样的:

  • 为 3 个不同的出口创建 .hbs.js 文件。
  • 在每个 JS 文件中,检查它正在使用的出口是否是设置 banner_location 的值。
  • 如果是,则显示横幅。如果不是,则隐藏横幅。
1 个赞

太棒了!看起来你决定使用 api.renderInOutlet,并为 outlet 名称使用了动态值 :chefs_kiss:

1 个赞