Hotwire Discussion

Triggering Turbo Frame with JS

@BKSpurgeon have you tried hooking a callback up to the turbo:submit-end event?

@dan thank you for your response.

The issue with turbo:submit-end is that I wish to act on the results after they are added to the dom. I was unable to get turbo:submit-end to do this?

<div data-controller="slideshow" data-slideshow-index-value="1"    />
  turbo_frame_tag "results"
    <form />  
    <li> results etc. </li>    
  end
  1. Form is submitted via javascript (i.e. within turbo_frame_tag)
  2. Controller is hit and results are returned.
  3. We want to perform some action on the results that are returned. i.e. a callback after the form submission and after everything’s rendered

I was unable to get turbo:submit-end to work on the rendered response of the form submission. Off the top of your head: is this the expected behaviour?

highlightCurrentSlide(event){  	
    this.slideTargets.forEach((element) => {         
     // the slideTargets get updated on the form submission
    //  the highlightCurrentSlide method seems to run before the
    // the results of the form submission are rendered.    
    }, this)
  }

Any advice would be much appreciated. chrs!

Hmm…turbo:submit-end only fires after your controller has responded. So I guess the issue here is the gap between getting that response and the HTML actually being put in the DOM. I wouldn’t expect that gap to be significant, though.

A couple of ideas (I haven’t tested any of them, mind):

The event exposes the FetchResponse via event.detail.fetchResponse (I think). FetchResponse has a responseHTML method that returns a promise. You could make your highlightCurrentSlide method async and on the first line await event.detail.fetchResponse.responseHTML().

Or (more of a long shot) I’d maybe try wrapping highlightCurrentSlide in a window.requestAnimationFrame callback.

Or, failing any of that, there’s probably a solution using MutationObserver and/or maybe giving those slideTargets their own controller (so you could just use connect).

1 Like

I think FrameElement.loaded could help you

In Stimulus controller:

// get frame    
const frame = document.getElementById("frame_id");
// submit form
this.formTarget.dispatchEvent(new CustomEvent('submit', { bubbles: true }));
// listen for frame rendering
frame.loaded.then(function (success) {
  // some actions after frame rendered
}, function (error) {});
3 Likes

That’s pretty cool! :clap:

I was getting Fetch API cannot load [url here] due to access control checks.

errors in Safari with the polyfill.

But this works in Safari and Brave.

This actually seemed to do the trick for me in Safari / Firefox / Chrome. Curious if this is the best approach?

After moving to using the new importmap-rails gem and failing to get rails-ujs working I submit my forms using -

        Turbo.navigator.submitForm(this.formTarget)

Ok, I couldn’t underand, why all the replies were so positive, as “nothing” didn’t work in my stimulus controller, after I upgraded turbo (previously I just “clicked” the submit button, what worked … until now).

But I’m really glad, that I read until the end of the topic, as those two solutions (actually it’s the same) are the only ones, which work for my controller, where I want to submit and register to the turbo:submit-end event:

from @defsdoor
Turbo.navigator.submitForm(this.formTarget)

and

import { navigator } from "@hotwired/turbo"
...
navigator.submitForm(this.element.closest("form"))

from @mrhead

much thanks to you both!

As it seems to be a bit of a hidden api, I hope that will least :wink:

1 Like