Utilizzo dei connettori Plugin Outlet da un tema o plugin

Discourse includes hundreds of Plugin Outlets which can be used to inject new content or replace existing contend in the Discourse UI. ‘Outlet arguments’ are made available so that content can be customized based on the context.

Choosing an outlet

To find the name of a plugin outlet, search Discourse core for “<PluginOutlet”, or use the plugin outlet locations theme component. (e.g. topic-above-posts).

Wrapper outlets

Some outlets in core look like <PluginOutlet @name="foo" />. These allow you to inject new content. Other outlets will ‘wrap’ an existing core implementation like this

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

Defining a connector for this kind of ‘wrapper’ outlet will replace the core implementation. Only one active theme/plugin can contribute a connector for a wrapper plugin outlet.

For wrapper plugin outlets, you can render the original core implementation using the {{yield}} keyword. This can be helpful if you only want to replace the core implementation under certain conditions, or if you would like to wrap it in something.

Defining the template

Once you’ve chosen an outlet, decide on a name for your connector. This needs to be unique across all themes / plugins installed on a given community. e.g. brand-official-topics

In your theme / plugin, define a new handlebars template with a path formatted like this:

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

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

The content of these files will be rendered as an Ember Component. For general information on Ember / Handlebars, check out the Ember guides.

For our hypothetical “brand official topics” connector, the template might look like

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

Some plugin outlets will automatically wrap your content in an HTML element. The element type is defined by @connectorTagName on the <PluginOutlet>.

Using outlet arguments

Plugin Outlets provide information about the surrounding context via @outletArgs. The arguments passed to each outlet vary. An easy way to view the arguments is to add this to your template:

{{log @outletArgs}}

This will log the arguments to your browser’s developer console. They will appear as a Proxy object - to explore the list of arguments, expand the [[Target]] of the proxy.

In our topic-above-posts example, the rendered topic is available under @outletArgs.model. So we can add the username of the team member like this:

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

Adding more complex logic

Sometimes, a simple handlebars template is not enough. To add Javascript logic to your connector, you can define a Javascript file adjacent to your handlebars template. This file should export a component definition. This functions just the same as any other component definition, and can include service injections.

Defining a component like this will remove the automatic connectorTagName wrapper element, so you may want to re-introduce an element of the same type in your hbs file.

In our topic-above-posts example, we may want to render the user differently based on the ‘prioritize username in ux’ site setting. A component definition for that might look something like this:

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

We can then update the template to reference the new getter:

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

Conditional rendering

If you only want your content to be rendered under certain conditions, it’s often enough to wrap your template with a handlebars {{#if}} block. If that’s not enough, you may want to use the shouldRender hook to control whether your connector template is rendered at all.

Firstly, ensure you have a .js connector definition as described above. Then, add a static shouldRender() function. Extending our example:

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";
  }
  // ... (any other logic)
}

Now the connector will only be rendered when the first post of the topic was created by a team member.

shouldRender is evaluated in a Glimmer autotracking context. Future changes to any referenced properties (e.g. outletArgs) will cause the function to be re-evaluated.

Introducing new outlets

If you need an outlet that doesn’t yet exist, please feel free to make a pull request, or open a topic in Dev.


This document is version controlled - suggest changes on github.

39 Mi Piace
(Superseded) Add header menu links
Using discourse's plugin outlets
Add HTML (Link) Next To Logo
What is the best way to integrate member applications?
Group Semantics
Developing Discourse Plugins - Part 2 - Connect to a plugin outlet
Native theme support
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
(not recommended) Overriding Discourse templates from a Theme or Plugin
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
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
Feedback on "on-discourse" javascript for setting up custom JS for each page?
Topic-timeline api.decorateWidget call has stopped working

Questo è ora deprecato, giusto?
Avviso di deprecazione: La definizione delle classi connettore tramite registerConnectorClass è deprecata. Vedere https://meta.discourse.org/t/32727 per pattern più moderni. [deprecation id: discourse.register-connector-class-legacy]

Esatto; puoi usare api.renderInOutlet invece. :slight_smile:

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

1 Mi Piace

Penso sia un po’ più complicato di così :sweat_smile:
https://github.com/Firepup6500/discourse-custom-profile-link/blob/master/common/head_tag.html

Non preoccuparti; vedrò cosa posso fare per aiutarti più tardi (devo dormire adesso). :smile:

1 Mi Piace

Grazie! (Mi dispiace per il codice disordinato, volevo solo che funzionasse l’ultima volta che l’ho toccato :sweat_smile:)

1 Mi Piace

Sono un po’ in difficoltà qui, ho ricevuto una notifica per il mio componente utilizzato nel file HEAD del mio tema. Non sono sicuro di come riscriverlo con api.renderInOutlet.

  const ajax = require('discourse/lib/ajax').ajax;
  const Topic = require('discourse/models/topic').default;
  // Usiamo ajax e il modello Topic da Discourse

  api.registerConnectorClass('above-main-container', 'featured-topics', {
    // above-main-container è l'outlet del plugin,
    // featured-topics è il nome del tuo componente personalizzato

    setupComponent(args, component) {

   // il resto del codice segue

Credo di aver provato a sostituire api.registerConnectorClass con api.renderInOutlet ma si è rotto. Non sono un esperto di codifica di temi qui. Grazie per qualsiasi aiuto.

Puoi vedere un esempio qui:

StatBanner è una classe nativa definita nella directory components:

Nel tuo caso, sarebbe api.renderInOutlet("above-main-container", YourClass)

Non credo che tu possa farlo nel file HEAD. Dovresti suddividere il tuo codice in più file.

Ti incoraggio a usare Discourse Theme CLI poiché sarà molto più facile sviluppare un componente del tema!

Il tuo componente del tema è pubblico?

3 Mi Piace

C’è un modo per ottenere la presa corrente nel mio file .js?

Non credo sia possibile.

In passato era possibile ispezionare parentView nei componenti classici.

Ma quella proprietà è stata deprecata.

1 Mi Piace

Cosa intendi con “ottenere l’output corrente”? Vuoi il nome dell’output? O qualcos’altro?

Ho trovato una soluzione al mio problema (qui), ma era qualcosa del tipo:

  • Creare file .hbs e .js per 3 diversi outlet.
  • In ogni file JS, verificare se l’outlet che sta utilizzando è il valore dell’impostazione banner_location.
  • Se lo è, mostrare il banner. Altrimenti, nascondere il banner.
1 Mi Piace

Fantastico! Sembra che tu abbia deciso di usare api.renderInOutlet, con un valore dinamico per il nome dell’outlet :chefs_kiss:

1 Mi Piace