How do I create multiple submit buttons for the same form in Rails?

Ruby on-RailsRubyForm Submit

Ruby on-Rails Problem Overview


I need to have multiple submit buttons.

I have a form which creates an instance of Contact_Call.

One button creates it as normal.

The other button creates it but needs to have a different :attribute value from the default, and it also needs to set the attribute on a different, but related model used in the controller.

How do I do that? I can't change the route, so is there a way to send a different variable that gets picked up by [:params]?

And if I do then, what do I do in the controller, set up a case statement?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

You can create multiple submit buttons and provide a different value to each:

<% form_for(something) do |f| %>
    ..
    <%= f.submit 'A' %>
    <%= f.submit 'B' %>
    ..
<% end %>

This will output:

<input type="submit" value="A" id=".." name="commit" />
<input type="submit" value="B" id=".." name="commit" />

Inside your controller, the submitted button's value will be identified by the parameter commit. Check the value to do the required processing:

def <controller action>
    if params[:commit] == 'A'
        # A was pressed 
    elsif params[:commit] == 'B'
        # B was pressed
    end
end

However, remember that this tightly couples your view to the controller which may not be very desirable.

Solution 2 - Ruby on-Rails

There is also another approach, using the formaction attribute on the submit button:

<% form_for(something) do |f| %>
    ...
    <%= f.submit "Create" %>
    <%= f.submit "Special Action", formaction: special_action_path %>
<% end %>

The code stays clean, as the standard create button doesn't need any change, you only insert a routing path for the special button: > formaction:
The URI of a program that processes the information submitted by the input element, if it is a submit button or image. If specified, it overrides the action attribute of the element's form owner. Source: MDN

Solution 3 - Ruby on-Rails

You can alternatively recognized which button was pressed changing its attribute name.

<% form_for(something) do |f| %>
    ..
    <%= f.submit 'A', name: 'a_button' %>
    <%= f.submit 'B', name: 'b_button' %>
    ..
<% end %>

It's a little bit uncomfortable because you have to check for params keys presence instead of simply check params[:commit] value: you will receive params[:a_button] or params[:b_button] depending on which one was pressed.

Solution 4 - Ruby on-Rails

Similar solution to one suggested by @vss123 without using any gems:

resources :plan do
  post :save, constraints: lambda {|req| req.params.key?(:propose)}, action: :propose
  post :save, constraints: lambda {|req| req.params.key?(:finalize)}, action: :finalize
end

Notice that I avoid using value and use input name instead since submit button value is often internationalized / translated. Also, I'd avoid using this too much since it will quickly clutter your routes file.

Solution 5 - Ruby on-Rails

We solved using advanced constraints in rails.

The idea is to have the same path (and hence the same named route & action) but with constraints routing to different actions.

resources :plan do
  post :save, constraints: CommitParamRouting.new("Propose"), action: :propose
  post :save, constraints: CommitParamRouting.new("Finalize"), action: :finalize
end

CommitParamRouting is a simple class that has a method matches? which returns true if the commit param matches the given instance attr. value.

This available as a gem commit_param_matching.

Solution 6 - Ruby on-Rails

An old question, but since I've been dealing with the same situation, I thought I'd post my solution. I'm using controller constants to avoid introducing a discrepancy between the controller logic and the view button.

class SearchController < ApplicationController
  SEARCH_TYPES = {
    :searchABC => "Search ABCs",
    :search123 => "Search 123s"
  }

  def search
    [...]
    if params[:commit] == SEARCH_TYPES[:searchABC]
      [...]
    elsif params[:commit] == SEARCH_TYPES[:search123]
      [...]
    else
      flash[:error] = "Search type not found!"]
      [...]
    end
  end
  [...]          
end

And then in the view:

<% form_for(something) do |f| %>
    [...]
    <%= f.submit SearchController::SEARCH_TYPES[:searchABC] %>
    <%= f.submit SearchController::SEARCH_TYPES[:search123] %>
    [...]
<% end %>

This way the text only lives in one place - as a constant in the controller. I haven't tried to figure out how to i18n this yet, however.

Solution 7 - Ruby on-Rails

I have a variable number of submit buttons on my form thanks to nested_form_fields, so just using the name wasn't enough for me. I ended up including a hidden input field in the form and using Javascript to populate it when one of the form submit buttons was pressed.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSatchelView Question on Stackoverflow
Solution 1 - Ruby on-RailsAnuragView Answer on Stackoverflow
Solution 2 - Ruby on-RailspatpirView Answer on Stackoverflow
Solution 3 - Ruby on-RailsmasciugoView Answer on Stackoverflow
Solution 4 - Ruby on-RailsTadas SasnauskasView Answer on Stackoverflow
Solution 5 - Ruby on-RailssiliconsenthilView Answer on Stackoverflow
Solution 6 - Ruby on-RailsDraknorView Answer on Stackoverflow
Solution 7 - Ruby on-RailsShawn WaltonView Answer on Stackoverflow