In a discussion titled “How to inject a service in discourse?” under the Dev category, JQ331 seeks to connect two plugin outlets via a service in a theme component or plugin, but encounters an error when trying to inject a service into a connector. fzngagan suggests using appEvents for inter-component communication and notes that Discourse’s Ember version may not support modern class syntax. david clarifies that connectors aren’t EmberObjects and thus can’t inject services — instead, one must use an Ember Component and inject the service there, referencing the whos-online plugin as an example. JQ331 confirms success in triggering service actions from a component but seeks to reverse the flow — triggering component actions from the service. david advises against this as non-standard, recommending appEvents instead. JQ331 explores using Evented directly, drawing parallels to Angular’s service pattern. david confirms appEvents wraps Evented and is suitable. JQ331 asks if custom appEvents can be created in plugins/themes, and fzngagan confirms yes, using appEvents.trigger. The thread concludes with consensus that appEvents is the recommended, idiomatic way to achieve cross-component communication in Discourse.
I want to use a service in a theme component I am building. If services are only available in plugins, I can build a plugin as well. Goal is to connect two different plugin outlets - press a button in one outlet, and have that button press picked up by the other outlet.
How can I properly register a service in Discourse? I’ve followed the topic here, and the related plugin code on github, but I’m not able to get it to work.
I think the missing piece is that my theme component is not recognizing the service. (Tried it in a plugin too, with same result).
Here’s some code I’ve tried:
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')
}
}
The problem here is that the ‘connector’ is not an EmberObject, and so you can’t inject things into it. Instead, you’ll need to create an Ember Component and inject it there.
Your ‘connector’ template would then look something like
{{my-component-name}}
Here’s an example of how that’s done in the whos-online plugin:
@david: thank you very much for this explanation and code examples. With those examples, I’ve been able to get it to work to click a button in in a component, and have it call an action in the service. That’s a big step forward.
Now I’m trying to sort through the other half–once an action is called in a service, have that trigger an action in a component. I figure it’s something like, in the service, importing the component and calling a function in that component (and/or subscribing to the action in the component). But I haven’t quite got the syntax yet.
Assuming that’s right, do you have any example syntax?
Calling a component method from a service isn’t really best practice, and Ember doesn’t provide an easy way to do it. There can be multiple instances of a component, so how would the service know which one to trigger the action on?
That said, sometimes it can be necessary to get something working within the confines of the Discourse plugin outlet system.
I’d recommend the same as @fzngagan - take a look at appEvents. They’re probably the cleanest way to trigger component logic from a service.
I had thought that is what evented was meant for, but I haven’t used it before. The goal is just to get an ipc-like setup where
user clicks button in component A
that click causes data to load in component B
I’m more familiar with Angular, where creating then subscribing to a service would be the general way to do it. But in Discourse the best way is appEvents?
Interesting! Thanks for the follow-up. I’m not partial to evented - never used it before and I was struggling a bit with the syntax in a Discourse service: export default class GreatStuffService extends Service.extends(Evented, {...})
is not quite right.
Haven’t used appEvents before either. Am I able to create a new appEvent in a plugin/theme-component that I can then subscribe to? Most examples I am finding are about subscribing to appEvents that are set out already in Discourse core.