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'.

1

6 Answers 6

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
Sign up to request clarification or add additional context in comments.

3 Comments

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!
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 this is what I'm trying to do right now, would love your thoughts in my question
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

Comments

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.

1 Comment

this is brilliant. it solves unability to redirect with POST method.
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.

2 Comments

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.
The only answer to this question 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" ) %>.
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

1 Comment

what about on "tap" on mobile devices?
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');
}   

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.