Utilizzo dei Plugin Outlet Connectors da un Tema o Plugin

Discourse include centinaia di Plugin Outlet che possono essere utilizzati per iniettare nuovi contenuti o sostituire contenuti esistenti nell’interfaccia utente di Discourse. Gli ‘Outlet arguments’ (argomenti dell’outlet) sono resi disponibili in modo che il contenuto possa essere personalizzato in base al contesto.

Scelta di un outlet

Per trovare il nome di un plugin outlet, cerca nel core di Discourse “<PluginOutlet” oppure utilizza il componente tema plugin outlet locations. (es. topic-above-posts).

Wrapper outlets

Alcuni outlet nel core assomigliano a <PluginOutlet @name="foo" />. Questi ti permettono di iniettare nuovi contenuti. Altri outlet ‘avvolgeranno’ un’implementazione core esistente in questo modo

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

Definire un connettore per questo tipo di outlet ‘wrapper’ sostituirà l’implementazione core. Solo un tema/plugin attivo può contribuire con un connettore per un plugin outlet wrapper.

Per i plugin outlet wrapper, puoi renderizzare l’implementazione core originale usando la parola chiave {{yield}}. Questo può essere utile se vuoi sostituire l’implementazione core solo in determinate condizioni, o se desideri avvolgerla in qualcos’altro.

Definizione del template

Una volta scelto un outlet, decidi un nome per il tuo connettore. Questo deve essere univoco tra tutti i temi/plugin installati su una data community. es. brand-official-topics

Nel tuo tema/plugin, definisci un nuovo template handlebars con un percorso formattato in questo modo:

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

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

Il contenuto di questi file verrà renderizzato come un Ember Component. Per informazioni generali su Ember/Handlebars, consulta le guide di Ember.

Per il nostro ipotetico connettore “brand official topics”, il template potrebbe apparire così:

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

Alcuni plugin outlet avvolgeranno automaticamente il tuo contenuto in un elemento HTML. Il tipo di elemento è definito da @connectorTagName sul <PluginOutlet>.

Utilizzo degli argomenti dell’outlet

I Plugin Outlet forniscono informazioni sul contesto circostante tramite @outletArgs. Gli argomenti passati a ciascun outlet variano. Un modo semplice per visualizzare gli argomenti è aggiungere questo al tuo template:

{{log @outletArgs}}

Questo stamperà gli argomenti nella console degli strumenti per sviluppatori del tuo browser. Appariranno come un oggetto Proxy - per esplorare l’elenco degli argomenti, espandi il [[Target]] del proxy.

Nel nostro esempio topic-above-posts, il topic renderizzato è disponibile sotto @outletArgs.model. Quindi possiamo aggiungere il nome utente del membro del team in questo modo:

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

Aggiunta di logica più complessa

A volte, un semplice template handlebars non è sufficiente. Per aggiungere logica Javascript al tuo connettore, puoi definire un file Javascript adiacente al tuo template handlebars. Questo file dovrebbe esportare una definizione di componente. Questo funziona esattamente come qualsiasi altra definizione di componente e può includere iniezioni di servizi.

Definire un componente in questo modo rimuoverà l’elemento wrapper automatico connectorTagName, quindi potresti voler reintrodurre un elemento dello stesso tipo nel tuo file hbs.

Nel nostro esempio topic-above-posts, potremmo voler visualizzare l’utente in modo diverso in base all’impostazione del sito ‘prioritize username in ux’. Una definizione di componente per questo potrebbe apparire più o meno così:

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

Possiamo quindi aggiornare il template per fare riferimento al nuovo 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>

Rendering condizionale

Se vuoi che il tuo contenuto venga renderizzato solo in determinate condizioni, spesso è sufficiente avvolgere il tuo template con un blocco {{#if}} di handlebars. Se questo non è sufficiente, potresti voler usare la funzione shouldRender per controllare se il template del tuo connettore debba essere renderizzato affatto.

Innanzitutto, assicurati di avere una definizione di connettore .js come descritto sopra. Quindi, aggiungi una funzione statica static shouldRender(). Estendendo il nostro esempio:

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";
  }
  // ... (qualsiasi altra logica)
}

Ora il connettore verrà renderizzato solo quando il primo post del topic è stato creato da un membro del team.

shouldRender viene valutato in un contesto di autotracking Glimmer. Modifiche future a qualsiasi proprietà referenziata (es. outletArgs) causeranno la rivalutazione della funzione.

Introduzione di nuovi outlet

Se hai bisogno di un outlet che non esiste ancora, sentiti libero di creare una pull request o aprire un topic in Dev.


Questo documento è controllato tramite versione - suggerisci modifiche su github.

39 Mi Piace
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

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