javascriptruby-on-railsstimulusjsturbo-rails

Turbo response to render javascript alert?


I am refactoring a Rails controller for turbo and stimulus, and trying to understand how to do things in Turbo that the app does currently with JS response templates.

On a things#show page, I have a button to request "previous versions" of a thing text record.

Currently, in Rails UJS, this button sends a GET JS request to a things/versions_controller#show action. The action responds by setting an ivar for the @versions (containing the text of the versions, pulled from the db), and renders a one-line js template:

# things/versions/show.js.erb

alert('<%= @versions %>');

With Turbo, i get it, the template has to simply render turbo_stream tags and update/append/replace page elements. An alert is outside that scope.

Is there maybe a way to pass the ruby ivar @versions directly to a Stimulus controller, that could show the js alert? The only way I can imagine it, the Stimulus controller would handle the click, make a fetch request to the things/versions_controller#show action (which would give an HTML response), and handle the response itself. No Turbo.

I guess I could have the turbo response print a hidden turbo_frame containing the @versions in the page, and attach that frame to a stimulus controller that would throw the text in an alert as soon as the frame is connected to the DOM. (?) But that seems overly contrived.

I'm really mostly looking to understand paradigms, to find the most idiomatic way to do things like this, if there is one. Also, I realize alerts are crude, and I could do this using a modal render.


Solution

  • In addition to other answers, you can make a custom Turbo Stream action to make alert action nice and reusable:

    // app/javascript/application.js
    
    Turbo.StreamActions.alert = function () {
      alert(this.templateContent.textContent)
    };
    
    # config/initializers/turbo_stream_actions.rb
    
    # this is optional but makes it much cleaner
    module CustomTurboStreamActions
      def alert text
        action(:alert, "#", text)
      end
    
      ::Turbo::Streams::TagBuilder.include(self)
    end
    

    Use it in your controllers as a response to TURBO_STREAM requests:

    respond_to do |format|
      format.turbo_stream do
        # without CustomTurboStreamActions module
        # render turbo_stream: turbo_stream.action(:alert, "#", @versions)
    
        # with CustomTurboStreamActions
        render turbo_stream: turbo_stream.alert(@versions)
      end
    end
    

    https://turbo.hotwired.dev/handbook/streams#custom-actions