69

Say I have an Article model, and in the article 'new' view I have two buttons, "Publish" and "Save Draft".

My question is how can I know which button is clicked in the controller.

I already have a solution but I think there must be a better way. What I currently used in the view is:

<div class="actions">
  <%= f.submit "Publish" %>
  <%= f.submit "Save Draft", :name => "commit" %>
</div>

So in the controller, I can use the params[:commit] string to handle that action.

def create
  @article = Article.new(params[:article])
  if params[:commit] == "Publish"
    @article.status = 'publish'
    // detail omitted
  end

  @article.save
end

But I think using the view related string is not good. Could you tell me another way to accomplish this?

UPDATE: Since these buttons are in the same form, they're all going to the 'create' action, and that's OK for me. What I want is to handle that within the create action, such as give the Article model a 'status' column and holds 'public' or 'draft'.

kinopyo
  • 1,667
  • 3
  • 19
  • 26
  • 1
    possible duplicate of [How do I create multiple submit buttons for the same form in Rails?](http://stackoverflow.com/questions/3027149/how-do-i-create-multiple-submit-buttons-for-the-same-form-in-rails) – Taryn East Jun 23 '15 at 04:32

6 Answers6

80

This was covered in Railscast episode 38. Using the params hash to detect which button was clicked is the correct approach:

View:

<%= submit_tag 'Create' %>
<%= submit_tag 'Create and Add Another', name: 'create_and_add' %>

Controller:

if params[:create_and_add]
  # Redirect to new form, for example.

else
  # Redirect to show the newly created record, for example.
end
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
John Topley
  • 113,588
  • 46
  • 195
  • 237
  • I've been watching Railscasts episode for a week but I definitely missed this one. It seems to be the same as my code, both use params hash, but it did a better work and I also improved my code. Thanks a lot! – kinopyo Jul 26 '10 at 16:30
  • This gets pretty complicated if you have a `_form` partial that's used within both the **new** and **edit** actions. In that case you'd have to set up the above controller logic in both the **create** and **update** actions, to account for the cases where the user is creating a new object or editing an existing one. – jayp Dec 31 '16 at 18:03
  • @jayp this is what I'm trying to do right now, would love your thoughts in [my question](http://stackoverflow.com/q/42154972/2909897) – mbigras Feb 10 '17 at 17:16
9

it can also be done on the form_for helper like this

 <%= f.submit "Publish",name: "publish", class: "tiny button radius success" %>
 <%= f.submit 'Mark as Draft', name: "draft", class: "tiny button radius " %>

and the logic is the same on the controller

   if params[:publish]
      // your code
   elsif params[:draft]
      // your code
   end
Petros Kyriakou
  • 5,214
  • 4
  • 43
  • 82
8

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.

siliconsenthil
  • 1,380
  • 1
  • 14
  • 25
1

I remember coming across this problem once. You cannot keep two buttons and then call some action based on the params[:commit]. the submit button onclick is going to call the url the form refers to. There are certain bad ways to get the desired behavior. Keep a button to call the action the form refers to and to get another button to call a action, I used a link_to and then changed the styles to match a button. Also, alternatively you can use jQuery to change the url the form would call, hence deciding what action is invoked at run-time. Hope this helps.

Alok Swain
  • 6,409
  • 5
  • 36
  • 57
  • Sorry I forgot to mention that I was trying to handle that in the same create action, I've updated the question. But I also learned your tips on how to use jQuery to change the url of the form, it's helpful. Thanks. – kinopyo Jul 26 '10 at 06:26
  • The only answer to [this question](http://stackoverflow.com/questions/3086609/ruby-on-rails-how-to-have-multiple-submit-buttons-going-to-different-methods-m) works for me, and it does indeed use the value of `params[:commit]` in the controller to distinguish which button was pressed. That is the string "X" when the submit button is defined in the view as `<%= submit_tag( "X" ) %>`. – Teemu Leisti Mar 10 '12 at 20:21
0

You could also set some data attributes on the submit buttons and use JavaScript to change out the form action on click of one of the buttons

dennismonsewicz
  • 25,132
  • 33
  • 116
  • 189
0

usually i using the suggestion given by John Topley (see answer above). another way is using JQuery /JS changing the form action attribute- upon clicking the submit button example:

form_tag({} ,:method => 'post', :id => 'reports_action') do 
   .......
   ....... 
  submit_tag 'submit',  :onclick => "return changeAction();"
end

and then .....

function changeAction(){
   $('#reports_action').attr('action','my_new_action');
}   
yonatan
  • 1
  • 1