ruby-on-railshamlturbo

Rails turbostream: is it possible to add a css class dynamically to an element?


I'm upgrading an application from rails 6 to 7 and replacing ujs with turbostreams.

The current ujs

$('#sightings_by_type').html("<%= escape_javascript (render partial: '/member/spotting_sessions/sightings_by_stock_type', locals: { editable: true }) %>")
$('#sighting_added_confirmation').css('display', 'block')
$('#sighting_added_confirmation').fadeOut(2000)

I'd like to replace the fadeOut call, and changing display, with a css class I've got available. However I can't figure out how I should add the class by using the turbo_stream.haml file. It seems that the turbo_stream can act upon the entire contents of an element (replacing a div, etc) but not down to the low level of changing attributes of an element.

Is turbo_streams the wrong way to tackle this?

I could put the logic into the stimulus controller but that doesn't feel the appropriate place for that.

This is the add_sighting.turbo_stream.haml so far:

= turbo_stream.update "sighting_details", partial: "/member/spotting_sessions/sightings_by_stock_type", locals: { editable: true }

Solution

  • You can use javascript in your turbo streams:

    <%= turbo_stream.update "sighting_details", partial: "/member/spotting_sessions/sightings_by_stock_type", locals: { editable: true } %>
    <%= turbo_stream.after "sighting_details" do %>
      <script charset="utf-8">
        $('#sighting_added_confirmation').css('display', 'block')
        $('#sighting_added_confirmation').fadeOut(2000)
      </script>
    <% end %>
    

    You can make custom turbo stream actions:

    // app/javascript/application.js
    
    Turbo.StreamActions.style = function () {
      this.targetElements.forEach((target) => {
        target.style = this.templateContent.textContent
      });
    };
    
    <%= turbo_stream.action :style, :sighting_details, "display: block;" %>
    

    Optionally, you can make it a method:

    # config/initializers/turbo_stream_actions.rb
    
    module TurboStreamActions
      def style(...)
        action(:style, ...)
      end
    
      Turbo::Streams::TagBuilder.include(self)
    end
    
    <%= turbo_stream.style :sighting_details, "display: block;" %>