I have a JS plugin in Shopware 6, that subscribes to two events of another JS plugin:
One that fires after the user interacts with the page and one that fires at the end of init()
.
The latter does nothing. I am confused because the other event works just fine.
Both $emitter.publish
have the same argument. In my plugin, they use the same method:
// main.js
import MyPlugin from "./my-plugin/my-plugin.plugin";
const PluginManager = window.PluginManager;
PluginManager.register("MyPlugin", MyPlugin);
// ./my-plugin/my-plugin.plugin.js
import Iterator from "src/helper/iterator.helper";
export default class MyPlugin extends window.PluginBaseClass {
init() {
const configuratorPlugins =
window.PluginManager.getPluginInstances("TargetPlugin");
Iterator.iterate(configuratorPlugins, (instance) => {
// nope ...
instance.$emitter.subscribe("init", this.onEvent.bind(this));
// working ...
instance.$emitter.subscribe(
"otherEvent",
this.onEvent.bind(this)
);
});
}
onEvent(event) {
// do it ...
}
}
// target-plugin.plugin.js
init() {
// ...
this.$emitter.publish('init', this.createCurrentState());
}
The init
event is not inside an if statement. I tested this.createCurrentState()
with a console.log()
right before the event and that works.
Also, I looked into PluginManager.getPluginList()
. The other plugin is in the list before my plugin gets registered – if that helps.
Don’t know what I am doing wrong. That plugin is the only one which publishes an event inside init()
.
2
Answers
Looking at the code snippets provided and the behavior you are seeing, some possible issues and steps to troubleshoot are:
Event Subscription Timing
From the fact that "otherEvent" subscription works, it means that your MyPlugin initialization is correct and it can subscribe to events from TargetPlugin instances. However, the subscription to the "init" event doesn’t work reliably.
Plugin Registration Order
You said you checked PluginManager.getPluginList() and confirmed that TargetPlugin is already in the list prior to the registration of MyPlugin. Good, because that way, when MyPlugin initializes and it subscribes to events from TargetPlugin instances, the instances of TargetPlugin should have been already created.
Ensure event publishing in TargetPlugin
You mentioned that TargetPlugin publishes an "init" event in its init() method. This new publishing of the event shall occur only after the plugin has been completely initialized and its internal state, for example this.createCurrentState(), is available for subscribers.
Check for Asynchronous Initialization
Sometimes, plugins or components just initialize themselves asynchronously. In this case, the "init" event might well be triggered before TargetPlugin or its components have finished initialization and event subscription if initialization of TargetPlugin or its components occurs asynchronously.
In such scenarios, proper synchronization could be attained by potentially using promises or async/await patterns if your plugin system allows for these to make sure that subscriptions are attempted only after all plugins have been initialized.
Notice the possible change of the initialization in your MyPlugin class to avoid asynchrony and make event-handling more robust:
In my opinion, the problem is exactly this:
A race condition with the initialization of the plugin. You are setting the listener for event
"init"
inside MyPlugininit()
method which is called after TargetPlugininit()
. So at the moment ofthis.$emitter.publish('init', this.createCurrentState());
MyPlugin hasn’t subscribed yet.
Possible solutions:
Both solutions imply an edit to the register plugin logic.
The first i think it’s not much consistent, very prone to error and not clear to other devs.
With the latter, when you register the plugin, you can pass an options object
Then in MyPlugin class you override the constructor, which is called for each plugin always before the initialization of all the others.
I haven’t tested the code, let me know if this idea helped.