¿Cómo inyectar un servicio en Discourse?

Quiero usar un servicio en un componente de tema que estoy creando. Si los servicios solo están disponibles en plugins, también puedo crear un plugin. El objetivo es conectar dos “outlets” (salidas) diferentes de plugins: presionar un botón en una salida y que esa pulsación de botón sea captada por la otra salida.

¿Cómo puedo registrar correctamente un servicio en Discourse? He seguido el tema aquí, y el código relacionado del plugin en GitHub, pero no he podido hacerlo funcionar.

Creo que la pieza que falta es que mi componente de tema no reconoce el servicio. (Lo he intentado también en un plugin, con el mismo resultado).


Aquí hay algo de código que he intentado:

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()
     }
  }
}

Cuando llamo a la acción “buttonPressInOutlet()”, obtengo el error: Uncaught TypeError: Cannot read properties of undefined (reading 'call').

¿Qué más se requiere?

Quizás quieras ver cómo están escritos otros servicios. No creo que la sintaxis x extends y funcione todavía en la versión de Ember que usa Discourse.

Además, quizás quieras ver la API appEvents para comunicarte entre dos componentes.

2 Me gusta

El problema aquí es que el ‘conector’ no es un EmberObject, por lo que no puedes inyectar cosas en él. En su lugar, necesitarás crear un Componente Ember e inyectarlo allí.

Tu plantilla ‘connector’ se vería algo así:

{{my-component-name}}

Aquí tienes un ejemplo de cómo se hace en el plugin whos-online:

Tenemos un servicio en el núcleo:

Una plantilla de conector en el plugin

La definición del componente:
https://github.com/discourse/discourse-whos-online/blob/main/assets/javascripts/discourse/components/whos-online.js#L6-L7

(o puedes usar export default Component.extend({ whosOnline: service() }) si lo prefieres)

Y la plantilla del componente:
https://github.com/discourse/discourse-whos-online/blob/main/assets/javascripts/discourse/templates/components/whos-online.hbs

3 Me gusta

No había pensado en eso. ¡Gracias por la idea!


@david: muchas gracias por esta explicación y ejemplos de código. Con esos ejemplos, he podido hacer que funcione para hacer clic en un botón en un componente y que llame a una acción en el servicio. Ese es un gran paso adelante.

Ahora estoy tratando de resolver la otra mitad: una vez que se llama a una acción en un servicio, que active una acción en un componente. Supongo que es algo así como, en el servicio, importar el componente y llamar a una función en ese componente (y/o suscribirse a la acción en el componente). Pero aún no he entendido bien la sintaxis.

Suponiendo que eso sea correcto, ¿tienes algún ejemplo de sintaxis?

Llamar a un método de componente desde un servicio no es realmente una buena práctica, y Ember no ofrece una forma fácil de hacerlo. Puede haber múltiples instancias de un componente, entonces, ¿cómo sabría el servicio cuál activar la acción?

Dicho esto, a veces puede ser necesario hacer que algo funcione dentro de los confines del sistema de outlets de plugins de Discourse.

Recomendaría lo mismo que @fzngagan: échale un vistazo a appEvents. Probablemente sea la forma más limpia de activar la lógica del componente desde un servicio.

2 Me gusta

Había pensado que para eso servía evented, pero no lo he usado antes. El objetivo es simplemente tener una configuración similar a la de ipc donde

  • el usuario hace clic en un botón en el componente A
  • ese clic hace que los datos se carguen en el componente B

Estoy más familiarizado con Angular, donde crear y luego suscribirse a un servicio sería la forma general de hacerlo. Pero en Discourse, ¿la mejor manera es appEvents?

¡Exacto! appEvents es un envoltorio para Evented. Si prefieres usar Evented directamente, también está bien :+1:

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

1 me gusta

:slight_smile: ¡Interesante! Gracias por el seguimiento. No tengo preferencia por evented; nunca lo he usado antes y me estaba costando un poco con la sintaxis en un servicio de Discourse:
export default class GreatStuffService extends Service.extends(Evented, {...})
no es del todo correcto.

Tampoco he usado appEvents antes. ¿Puedo crear un nuevo appEvent en un plugin/componente temático al que luego pueda suscribirme? La mayoría de los ejemplos que encuentro son sobre cómo suscribirse a appEvents que ya están definidos en el núcleo de Discourse.

1 me gusta

Sí, puedes hacerlo. Busca appEvents.trigger

2 Me gusta