Cannot dispatch Event in connect()

I’ve been trying to link behaviors between controllers.

Let’s say I have a list of div.block, each linked to a blocks stimulus controller.
When I update one of this block, i’m replacing the HTML with this.element.outerHTML which by definition disconnect this element, and creates a new one.
When I do update a block, I want to warn another controller (which wrappes almost the whole page) an update happened. For that, I added data-action='update->othercontroller#update' and I wanted to dispatch an event (update) like this: this.element.dispatch(new Event(update)); when this new element connect().
But for some reasons, the event is not dispatched.

When I load the page for the first time, the event is dispatched as the blocks gets connected to the controller.
But, when I update one block (replacing a block by another one), connect is triggered, but not the event dispatch line inside…

There is something I can’t see here.
If anyone has any idea where to look.
thx a lot

@db0sch

for a similar usecase I am using a different approach by getting the instance of the parent controller with this function

initialize() {
  this.parentController = this.getControllerByIdentifier("parentController");
}

getControllerByIdentifier(identifier) {
    return this.application.controllers.find(controller => {
      return controller.context.identifier === identifier;
    });
  }

Maybe this can help
or can you post a full example to test

ok. so you’re calling the parent controller inside the child one. Nice one.
But I wanted to avoid that. I’m afraid this will lead to spaghetti code at some point :wink:
The code looks too complicated I think. And sending an event looks more straightforward to me.

I’ll keep that in mind though.
thx

ok in my case the parent/children controllers are really tight together but I understand your point about spaghetti.

About your code how do you listen for the events? You dispatch events at the element level this.element.dispatchEvent that means you need to listen for the event at this element level from your higher controller. When you replace this element with a new one you need to update your listener… this is quite complex and probably your problem.

the easiest solution for me would be to dispatch your events at the document level

document.dispatchEvent(new Event("my-update"));

and in your higher controller have

document.addEventListener("my-update", (e) => {
  console.log("update", e);
});

With a second thought as you are using a stimulus action as your listener this has worked for me in the past

window.dispatchEvent(new Event("update"));

and in your parent controller add an action tag

data-action='update@window->othercontroller#update'

It would probably work. But not a big fan of dispatching an event on the window level.
It’s a bit too much, don’t you think?
I managed to dispatch the event in another action…
But for some reason, not in the connect… don’t know why

Ok here is a codepen https://codepen.io/adrienpoly/pen/ejwyde?editors=1010#0
The main issue in your initial code was the way to create the event. When you create the event with new Event("update") it has a bubbling false property. As you emit the event from this.element this event is only visible within the element as it does not bubbles (so not visible from the parent controller).

In the code pen you can see that when you create an event with bubble true, then I can dispatch it from connect() and catch it from the parent controller.

I am still confused on how you could get it to work from another action vs connect as for me the problem was really the scope of your dispatch event. Maybe I missed something

This issue seems has been fixed:


But it hasn’t been released.

Confirming that this is isn’t fixed in the latest stimulus version.
As a workaround, you can wrap the dispatch in a setTimeOut :

export default class extends Controller {
  static values = {url: String }
    connect() {
        // this won't arrive anywhere 
        // this.dispatch("finishedEstimate",{detail :{geojsonUrl:this.urlValue}});
        //  console.log("Estimator dispatched event");
        // this will 
        setTimeout(() =>{
            this.dispatch("finishedEstimate",{detail :{geojsonUrl:this.urlValue}});
        },1);
    }
    [...]

1 Like

Ditto. Though, I found it a bit more nuanced in my case ~> if I dispatch without any additional information via the detail object, the event is dispatched and captured on the other side perfectly fine.

But as soon as I add anything to details, even a simple “test” string, nada.

setTimeout corrects it!