Discourse 包含数百个插件插槽(Plugin Outlet),可用于将新内容注入 Discourse UI 或替换现有内容。“插槽参数”(Outlet arguments)可供使用,以便可以根据上下文定制内容。
选择插槽
要查找插件插槽的名称,请在 Discourse 核心代码中搜索 “<!--PluginOutlet”,或使用 插件插槽位置 主题组件。(例如 topic-above-posts)。
包装插槽
核心中的一些插槽看起来像 <!--PluginOutlet @name="foo" /-->。这些允许您注入新内容。其他插槽将“包装”现有的核心实现,如下所示:
<!--PluginOutlet @name="foo"-->
核心实现
<!--/PluginOutlet-->
为这类“包装”插槽定义一个连接器(connector)将替换核心实现。只有一个活动的 theme/plugin 可以为一个包装插件插槽贡献一个连接器。
对于包装插件插槽,您可以使用 {{yield}} 关键字来渲染原始的核心实现。如果您只希望在特定条件下替换核心实现,或者希望将其包装在某些内容中,这会很有帮助。
定义模板
选择插槽后,决定连接器的名称。这在给定社区安装的所有主题/插件中必须是唯一的。例如 brand-official-topics
在您的主题/插件中,定义一个新的 Handlebars 模板,其路径格式如下:
![]()
{theme}/javascripts/discourse/connectors/{outlet-name}/{connector-name}.hbs
![]()
{plugin}/assets/javascripts/discourse/connectors/{outlet-name}/{connector-name}.hbs
这些文件的内容将被渲染为一个 Ember 组件。有关 Ember/Handlebars 的一般信息,请参阅 Ember 指南。
对于我们假设的“品牌官方话题”连接器,模板可能如下所示:
<div class="alert alert-info">
此话题由
<a href="https://discourse.org/team">Discourse 团队</a>
的成员创建
</div>
一些插件插槽会自动将您的内容包装在 HTML 元素中。元素类型由 <!--PluginOutlet--> 上的 @connectorTagName 定义。
使用插槽参数
插件插槽通过 @outletArgs 提供有关周围上下文的信息。传递给每个插槽的参数各不相同。查看参数的简单方法是将此添加到您的模板中:
{{log @outletArgs}}
这将把参数记录到您浏览器的开发者控制台。它们将显示为一个 Proxy 对象——要查看参数列表,请展开代理的 [[Target]]。
在我们的 topic-above-posts 示例中,渲染的主题可在 @outletArgs.model 下找到。因此,我们可以像这样添加团队成员的用户名:
<div class="alert alert-info">
此话题由
{{@outletArgs.model.details.created_by.username}}
(<a href="https://discourse.org/team">Discourse 团队</a>的成员)创建
</div>
添加更复杂的逻辑
有时,简单的 Handlebars 模板是不够的。要向连接器添加 Javascript 逻辑,您可以定义一个与 Handlebars 模板相邻的 Javascript 文件。该文件应导出一个组件定义。这与任何其他组件定义的功能相同,可以包括服务注入。
以这种方式定义组件将移除自动的 connectorTagName 包装元素,因此您可能希望在 hbs 文件中重新引入相同类型的元素。
在我们的 topic-above-posts 示例中,我们可能希望根据“在用户体验中优先显示用户名”站点设置以不同方式渲染用户。该组件定义可能如下所示:
.../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;
}
}
}
然后我们可以更新模板以引用新的 getter:
<div class="alert alert-info">
此话题由
{{this.displayName}}
(<a href="https://discourse.org/team">Discourse 团队</a>的成员)创建
</div>
条件渲染
如果您只希望在特定条件下渲染内容,通常只需用 Handlebars 的 {{#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 自动跟踪(autotracking)上下文中进行评估。对任何引用的属性(例如 outletArgs)的未来更改将导致函数重新评估。
引入新插槽
如果您需要一个尚不存在的插槽,请随时提交 pull request,或在 Dev 频道中开一个话题。
本文档是版本控制的——请在 github 上建议更改。