How to call stimulus function in turbo_stream response?

Hey,
I have stimulus function.

reset() {
 ...
}

And I also have turbo_stream response

  = turbo_stream.append("bulk-action-modal") do
    = content_tag :div, "", data: {action: "turbo:load->bulk-action#reset"}

How to call this function on dom connection?

Right now the reset method is not called. I am just wondering which event should I trigger to call this method.

I could create another stimulus controller and then put the code into connect method, but It would be more code to write. I would like to avoid this.

I added reset target and then use resetTargetConected

  resetTargetConnected(element) {
    this.reset();
    element.remove();
  }
  = turbo_stream.append("bulk-action-modal") do
    = content_tag :div, "", data: {bulk_action_target: 'reset'}
1 Like

How does your controller know to call resetTargetConnected() when it sees that data in an appended object?

When I was doing something similar, the only way I could get it to work was to have the controller save itself in the global window, then added a streamactions js to call it:

StreamActions.check_convo = function () {
  const text = this.getAttribute( "text" );
  if ( typeof cc != "undefined" ) cc.check_convo( text );
}

It works because stimulus automatically observes the DOM for changes within the controller’s scope. When a new element is appended and it has a data-[controller]-target, stimulus calls the corresponding targetConnected() callback.

So in the case of OP, when the div is appended by the turbo_stream, stimulus will automatically trigger resetTargetConnected() which then calls reset and remove.

Useful feature baked into stimulus for free which often gets missed!

Nice, thanks. I’ve been struggling with sticking with the HTML-over-the-wire aspect of hotwire but then needing to also have more complex things happen (like adding boldface to a user’s name when there’s a new message from them, etc).

As with all coding really, it looks elegant when getting started but then once you have all the “fixes” there to make it actually work it’s easy to have it be a mess.

As with all coding really, it looks elegant when getting started but then once you have all the “fixes” there to make it actually work it’s easy to have it be a mess.

just break it up into smaller controllers.

I have controllers handling all kinds of target connected code like show toast messages, closing modals, reloading frames and updating counters, no matter which frame is updated.

For modals I use turbo:before-fetch-request to open the modal when its frame starts loading.
And turbo:frame-render to hide the loading icon and show the contents of the request.

Stimulus makes it real easy controlling the interface with frames.

So when one of your controllers wants to show a toast, it’ll append a div with the stimulus controller to the Dom?

Modal - wouldn’t the turbo frame load the stimulus object/controller? So how would it create itself or intercept before-fetch-request.

So when one of your controllers wants to show a toast, it’ll append a div with the stimulus controller to the Dom?

The frame response contains a div with a data-frame-target="messages". inside are divs with toasts having data-toast-target="message".

The frame controller appends the content to the toast container div with messagesTargetConnected().

The toast controller will pick up this change with messageTargetConnected() showing the new messages.

Modal - wouldn’t the turbo frame load the stimulus object/controller? So how would it create itself or intercept before-fetch-request.

The modal is already in the dom and containing a frame.
I check the target of turbo:before-fetch-request event, when it is the frame inside the modal i will call show() on the modal.
Same for turbo:frame-render, then I hide loading and show frame content.