简体   繁体   中英

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

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?

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.

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

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.

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.

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 .

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM