I just stared using stimulus and I’m having some trouble, converting my existing javascript to stimulus.
I have a class A that does things and a class B that does things.
In class A an instance of class B is created with some params.
So I wrote class A as a stimulus controller, which works just finde. But I integrated class B in that. Now I want to have class B as a separate controller and call it from that A stimulus controller. I have no idea how to do that.
I cannot use events, since there are no events that trigger my script. The controller A does its job when it’s initialized.
You can definitely use events. Events are the way to go in your case. And, it’s pretty easy to listen and dispatch events with stimulus. Let’s follow along with your case and create the classes.
class A extends Controller {
initialize() {
this.dispatch("custom-event-from-a-class", {
target: document.documentElement,
data: {
// data you want to send to class B
}
})
}
}
class B extends Controller {
onClassAEvent({ data }) {
// data dispatched from class A custom event
}
}
Now, with the classes being set up. You want to “listen” for custom-event-from-a-class from b element. Let’s say your markup looks like this
In the b element. We listen for a a:custom-event-from-a-class that targets the window via the @window descriptor. See Global Events
when a controller dispatches it’s event. Every controller that has the a:custom-event-from-a-class@window->b#onClassAEvent would react to it.
Now, A and B controller can communicate together. And vice-versa is true. If you want to interact with A controller from B, you need to dispatch an event from B, and listen to the event inside A controller.
That was really helpful to understand the dispatch. Thank you.
Still it is a bit confusing to me.
I found out that data-controller must in in the correct order, so data-controller=“a b” did not work, but data-controller=“b a” does. So far so good.
Now I don’t really get, what custom-event-from-a-class is there for… Code in custom-event-from-a-class is not executed that way. So at the moment it seems to be only helping for the dispatch (since I need a custom method, initialize doesn’t work).
If I want code from custom-event-from-a-class to be executed I have to call it (from initialize)…
But the main issue now is, that I need the return value from B->onClassAEvent in A. How can I access the return value?
I don’t think it has to be a controller.
It’s just a class, that checks if intersectionObserver is present and returns an instance if so, otherwise “false”. It gets 2 params: the callback and the observer options.
I just don’t know what’s the right way to instantiate it now, stimulus style
that’s “B” … A is whatever needs this return value…
Don’t think, this needs to be a controller, since I don’t use it as such. I just need it for other classes to call it. I jus want it to integrate the way it’s supposed to in stimulus…
Ok thank you.
If I understand correctly, there is no special “stimulus way” to integrate this. I’m ending up, keeping my original observer class and creating a regular instance in my controller:
export class Observer {
constructor(observerCallback, observerOptions) {
this.callback = observerCallback ?? function() {};
this.options = observerOptions ?? {
root: null,
threshold: 0,
rootMargin: "0px, 0px, 0px, 0px",
};
return this.checkIfObserver();
};
checkIfObserver() {
if (!('IntersectionObserver' in window) ||
!('IntersectionObserverEntry' in window) ||
!('intersectionRatio' in window.IntersectionObserverEntry.prototype)) {
return false;
} else {
return new IntersectionObserver( this.callback, this.options);
}
};
}
and
import {Controller} from '@hotwired/stimulus';
import {Observer} from './observer.js'
export default class extends Controller {
initialize() {
this. this.observerCallback = ... ;
this.observerOptions = ... ;
const observer = new Observer(this.observerCallback, this.observerOptions);
...
}
}
Correct. I will mention a couple of additional things.
If the element assigned the controller might be removed and reinitialized in the DOM, you may want to use connect() instead of initialize().
If you are also using turbo (which acts like an SPA), you want to be careful of memory leaks caused by listeners and observers. To avoid memory leaks, add disconnect() to the stimulus controller with code that removes the listeners or observers.
First of all thank you so much for helping me understand stimulus!
you may want to use connect() instead of initialize().
yes, I read about that in the docs. So far initialize does the job for me.
If you are also using turbo
Turbo is actually the reason I started having a look into stimulus. Turbo killed my JavaScripts ( ) … so I wanted to start understanding what stimulus does etc.
Still not sure I really need / should use stimulus or turbo. Especially turbo seems “dangerous”… But I will try a few things and see what happens…
In the past I tried using TurboLinks (now Turbo) several times, each time I ran into enough issues which caused me to give up. It wasn’t until recently that I realized why.
Turbo Drive caches pages using HTML. Since most JavaScript libraries persist data in memory, this can cause weird behavior with turbo caching.
Stimulus easily persists data in HTML (data attributes). This is why stimulus works so well with Turbo. With stimulus you can also wrap libraries which save data in memory to store data in html.
Once I started using stimulus and understood how turbo worked, the entire Hotwire stack worked really well and no longer felt brittle. In fact, for me it feels more robust than the majority of the SPA frameworks out there.
Thanks so much for your help and sharing your experience. I’m definitely eager to learn more about stimulus and turbo. I’ll keep testing it. I’m certain I’ll ask for advice here again