Open link in a new tab

Content

Expand project menu

Stimulus in Plugin

Hei there!

I'm currently working on a plugin and I would like to use hotwire stimulus.

I don't see how to do this from the documentation or sample plugins.

When using the dynamic loading of the stimulus controller, the plugin's controller file is not found (not surprisingly).


To me it seems like the problem is that the stimulus OpApplicationController needs the dynamically loaded stimulus controller to be in the <op-core-root>/frontend/src/stimulus/controllers/dynamic directory.

However the plugin frontend linking symlinks to <op-core-root>/frontend/src/app/features/plugins.
So there is no way to import the symlinked stimulus controller files.

So I see two solutions:

  1. Symlinking the plugin controllers/dynamic directory to the OpenProject core's stimulus controllers directory (plus probably adjusting the OpApplicationController a bit)
  2. Using stimulus' application.register() function to add the plugins controller in a JS script in the plugin.

The first solution would be a lot nicer since it ties in nicely with the symlinking used for angluar already, but requires a change in OP.

The second approach feels a bit like a hack, but wouldn't require a change in OP.

I could make a merge request for option 1 if that is a favorable solution.

What is your opinion?

PS: I really like the OP code in general. It's challenging to understand, but I learn so much from it!

Message actions

Another solution I got working is to create a second stimulus instance.
This is of course very dissatisfying and I also loose the "dynamic" loading of stimulus controllers implemented in OP.

What I did is starting another stimulus application like this:

import { Application } from '@hotwired/stimulus';
import { HelloController }  from "./stimulus/controllers/dynamic/hello.controller";

const instance = Application.start();
window.StimulusCustom = instance;

instance.handleError = (error, message, detail) => {
  console.warn(error, message, detail);
};
instance.register('hello', HelloController);

and in my erb

<div data-controller="hello">
<input data-hello-target="name" type="text">
  <button data-action="click->hello#greet">Greet</button>
</div>

The hello.controller.ts ist the stimulus controller from the official tutorial example.

Message actions

Got into the same question: made a plugin with new pages in admin part and want to add some fancy "(Un)Check All" icons for tables like at workflows status matrix but don't want to start another stimulus instance.

Message actions

I haven't found any better solution than the one described above.

The frontend/module/main.ts registers all the stimulus controllers to a new stimulus instance just for the plugin.
And I keep all stimulus controllers in a subdirectory frontend/module/stimulus .
That's it.

# import the MyStimulusController

declare global {
    interface Window {
        StimulusIBU:Application
    }
}

const instance = Application.start()
window.StimulusPlugin = instance

instance.register("my-stimulus", MyStimulusController)

What can I say... it works. And it's the only way I got it to work.
And it's managable and extendable.
I haven't found any other plugins that use stimulus. The modules that are part of the OpenProject source (modules/in the OP code) all put their stimulus controllers into the frontend/src/stimulus/controllers/dynamic and not into their own module/xyz-module/frontend directory. So there's no solution for us to copy from there either.
I wish I could give you a more satisfying answer...

Message actions

Thanks for your answer, Niclas. I came with kinda same result while experimenting, unfortunately I'm not a frontend guy and can't do a lot. So your way is the best ATM i thing.

Message actions

Controller registry implemented by https://github.com/opf/openproject/pull/19493