Wie injiziere ich einen Service in Discourse?

Ich möchte einen Dienst in einer Theme-Komponente verwenden, die ich gerade erstelle. Wenn Dienste nur in Plugins verfügbar sind, kann ich auch ein Plugin erstellen. Ziel ist es, zwei verschiedene Plugin-Outlets zu verbinden – auf einen Button in einem Outlet zu klicken, und dieser Klick wird vom anderen Outlet aufgegriffen.

Wie kann ich einen Dienst in Discourse richtig registrieren? Ich habe dem Thema hier und dem zugehörigen Plugin-Code auf GitHub gefolgt, aber es funktioniert nicht.

Ich glaube, das fehlende Teil ist, dass meine Theme-Komponente den Dienst nicht erkennt. (Habe es auch in einem Plugin versucht, mit demselben Ergebnis).


Hier ist etwas Code, den ich ausprobiert habe:

javascripts/discourse/services/great-stuff.js:

import Service, { inject as service } from "@ember/service";

export default class GreatStuffService extends Service {
    call() {
        console.log('you called it')
    }
}

connectors/topic-list-after-title/sample-outlet.js.es6:

import { inject as service } from "@ember/service";

export default {
  greatStuff: service(),
  actions: {
      buttonPressInOutlet(){
         this.greatStuff.call()
     }
  }
}

Wenn ich die Aktion “buttonPressInOutlet()” aufrufe, erhalte ich den Fehler: Uncaught TypeError: Cannot read properties of undefined (reading 'call').

Was ist sonst noch erforderlich?

Sie sollten sich vielleicht ansehen, wie andere Dienste geschrieben sind. Ich glaube nicht, dass die x extends y-Syntax in der Ember-Version, die Discourse verwendet, bereits funktioniert.

Außerdem sollten Sie sich die appEvents-API ansehen, um zwischen zwei Komponenten zu kommunizieren.

2 „Gefällt mir“

Das Problem hier ist, dass der ‘connector’ kein EmberObject ist und man daher keine Dinge hineininjizieren kann. Stattdessen müssen Sie eine Ember-Komponente erstellen und diese dort injizieren.

Ihre ‘connector’-Vorlage würde dann etwa so aussehen:

{{my-component-name}}

Hier ist ein Beispiel, wie das im whos-online-Plugin gemacht wird:

Wir haben einen Service im Kern:

Eine Connector-Vorlage im Plugin

Die Komponenten-Definition:
https://github.com/discourse/discourse-whos-online/blob/main/assets/javascripts/discourse/components/whos-online.js#L6-L7

(oder Sie können export default Component.extend({ whosOnline: service() }) verwenden, wenn Sie möchten)

Und die Komponenten-Vorlage:
https://github.com/discourse/discourse-whos-online/blob/main/assets/javascripts/discourse/templates/components/whos-online.hbs

3 „Gefällt mir“

Daran hatte ich nicht gedacht. Danke für die Idee!


@david: Vielen Dank für diese Erklärung und die Codebeispiele. Mit diesen Beispielen konnte ich es zum Laufen bringen, auf eine Schaltfläche in einer Komponente zu klicken und eine Aktion im Dienst aufzurufen. Das ist ein großer Fortschritt.

Jetzt versuche ich, die andere Hälfte zu sortieren – sobald eine Aktion in einem Dienst aufgerufen wird, soll dies eine Aktion in einer Komponente auslösen. Ich stelle mir vor, dass es so etwas wie das Importieren der Komponente im Dienst und das Aufrufen einer Funktion in dieser Komponente ist (und/oder das Abonnieren der Aktion in der Komponente). Aber die Syntax habe ich noch nicht ganz herausgefunden.

Angenommen, das ist richtig, haben Sie ein Beispiel für die Syntax?

Das Aufrufen einer Komponentenmethode aus einem Dienst ist keine gute Vorgehensweise, und Ember bietet keine einfache Möglichkeit, dies zu tun. Es kann mehrere Instanzen einer Komponente geben, woher weiß der Dienst, welche er auslösen soll?

Dennoch kann es manchmal notwendig sein, etwas innerhalb des Discourse-Plugin-Outlet-Systems zum Laufen zu bringen.

Ich würde dasselbe wie @fzngagan empfehlen – werfen Sie einen Blick auf appEvents. Dies ist wahrscheinlich der sauberste Weg, um Komponentenlogik von einem Dienst aus auszulösen.

2 „Gefällt mir“

Ich dachte, dafür sei evented gedacht, aber ich habe es noch nicht verwendet. Das Ziel ist nur, ein IPC-ähnliches Setup zu erhalten, bei dem

  • der Benutzer auf eine Schaltfläche in Komponente A klickt
  • dieser Klick dazu führt, dass Daten in Komponente B geladen werden

Ich bin mit Angular vertrauter, wo das Erstellen und Abonnieren eines Dienstes der allgemeine Weg wäre. Aber in Discourse ist der beste Weg appEvents?

Genau! appEvents ist ein Wrapper für Evented. Wenn Sie Evented direkt verwenden möchten, ist das auch in Ordnung :+1:

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/services/app-events.js#L4

1 „Gefällt mir“

:slight_smile: Interessant! Danke für die Rückmeldung. Ich bin nicht an Evented gebunden – ich habe es noch nie benutzt und hatte ein wenig Schwierigkeiten mit der Syntax in einem Discourse-Service:
export default class GreatStuffService extends Service.extends(Evented, {...})
ist nicht ganz richtig.

Ich habe auch appEvents noch nie benutzt. Kann ich in einem Plugin/Theme-Komponente ein neues appEvent erstellen, das ich dann abonnieren kann? Die meisten Beispiele, die ich finde, handeln vom Abonnieren von appEvents, die bereits im Discourse-Kern festgelegt sind.

1 „Gefällt mir“

Ja, das können Sie. Suchen Sie nach appEvents.trigger.

2 „Gefällt mir“