Search code examples
javascriptruby-on-railsrubystimulusjs

Binding multiple actions to the same event with Stimulus


Does anyone know if it's possible to bind multiple actions to the same event with Stimulus in a Rails template?

I currently have this:

<%= form.text_area :content, value: '', id: "message-input",
                           data: { action: "input->message-form#inputChange input->form#autogrowInput 
                           keydown.enter->message-form#sendMessage keydown.enter->message-actions#clearReply 
                           keydown.ctrl+enter->message-form#addNewLine keydown.ctrl+enter->form#autogrowInput", message_form_target: "messageInput", form_target: "inputField" },
                           class: "form-input-no-label", autofocus: true %>

I want to avoid duplicating the event calls and chain multiple actions like this:

<%= form.text_area :content, value: '', id: "message-input",
                           data: { action: "input->message-form#inputChange form#autogrowInput
                           keydown.enter->message-form#sendMessage message-actions#clearReply
                           keydown.ctrl+enter->message-form#addNewLine form#autogrowInput", message_form_target: "messageInput", form_target: "inputField" },
                           class: "form-input-no-label", autofocus: true %>

But this causes the actions named to stop working.

Does anyone know if it's possible to avoid event duplication?


Solution

  • You'd be better off just formatting your code:

    <%= form.text_area :content,
      data: {
        action: %(
          input->message-form#inputChange
          input->form#autogrowInput
          keydown.enter->message-form#sendMessage
          keydown.enter->message-actions#clearReply
          keydown.ctrl+enter->message-form#addNewLine
          keydown.ctrl+enter->form#autogrowInput
        ),
        message_form_target: :messageInput,
        form_target: :inputField
      }
    %>
    

    This way you can quickly copy paste lines and move things around.


    Alternatively, you could make a helper:

    # app/helpers/application_helper.rb
    
    def data_action hash
      hash.map do |event, actions|
        actions.map do |controller, action|
          "#{event}->#{controller.to_s.dasherize}##{action}"
        end
      end.join(" ")
    end
    
    <%= form.text_area :content,
      data: {
        action: data_action(
          input:                {message_form: :inputChange, form: :autogrowInput},
          "keydown.enter":      {message_form: :sendMessage, message_actions: :clearReply},
          "keydown.ctrl+enter": {message_form: :addNewLine,  form: :autogrowInput}
        ),
        message_form_target: :messageInput,
        form_target: :inputField
      }
    %>