Getting a form to use :method => :delete (rails) - ruby-on-rails

I have a cart which contains many line_items. I'd like to have a "delete" button next to each line item that, upon clicked, removes the line_item from the cart.
I know I can do this with a button_to method, but I'd like to use form_for because I'd like to change the attributes of the line_item's parent object at the same time (each line_item also belongs to a course, and I'd like to tell the course parent that it's no longer in the cart).
Here's my code using form_for:
<%= form_for(line_item, :method => :delete, :remote => true) do |f| %>
<%= f.submit :value => "Delete" %>
<% end %>
The ruby documentation says that simply adding :method => :delete should work (http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for), but the rendered html isn't quite right. It's still
<input name="_method" type="hidden" value="put">
But it should be:
<input name="_method" type="hidden" value="delete">
What am I doing wrong?

Mark Needham has a blog post that talks about why :method => delete in form_for doesn't work. He says
It turns out that ‘form_for’ expects the ‘:method’ to be provided as part of the right hand
most argument as part of a hash with the key ‘:html’.
So you need to change your code from:
<%= form_for(line_item, :method => :delete, :remote => true) do |f| %>
to:
<%= form_for(line_item, :html => { :method => :delete, :remote => true }) do |f| %>
I tried it in a Rails 3.0 application, and the generated HTML was:
<input type="hidden" value="delete" name="_method">

Mine in HAML is just:
= form_with url: destroy_user_session_path, method: :delete do |form|
= form.submit "Sign out"

Related

Rails form_for sending params to controller action and not model

In the controller review_queue I have a custom action that posts a result to a target URL, I want to build a form for this action. I am not going to save any of the fields to the DB I am just going to pass them in the params to the post_review action.
def post_review
RestClient::Request.execute(:method => :post,
:url => Rails.application.secrets['target_url'],
:content_type => :json,
:payload => #result_params.merge!(params[:reasons]).to_json,
:headers => HEADERS)
end
In the view I have a form that will be filled out and on submit it should send up the reasons when the form is submited, I am setting the review_queue_id and the status in the form, since these are static, but the reasons should come from the textarea
<%= form_for(:review_queue, url: { action: 'post_review', :review_queue_id => #review_queue.id, :status => 'accepted'} ) do |f| %>
<div class='form-group'>
<label for='comment'>Please give a reason? (required)</label>
<%= f.text_area(:reasons, placeholder: 'Your commentns ...', rows: 9, class: 'form-control') %>
</div>
<div class='modal-footer'>
<%= f.submit 'Approve', class: 'btn btn-success btn-decission btn-modal-left-side' %>
<button type='button' class='btn btn-default' data-dismiss='modal'>Close</button>
</div>
<% end %>
error message:
NoMethodError - undefined method `reasons' for #<ReviewQueueApplication:0x007fa7ff7832d8>:
It seems as if rails is assuming the MVC architecture here, and assuming I want to pass the reasons to the review_queue model. there is no reasons column so it's dropping a no method error. Is there a way of specifying that the form is 'temporary' and only getting as far as the controller?
This seems like it should be a simple thing but there is some rails magic happening here.
NoMethodError - undefined method `reasons' for
ReviewQueueApplication:0x007fa7ff7832d8
form_for assumes that you are creating a form for a model object and expects the fields to be present in that specific model's table(in a normal situation).
You should be going with form_tag
<%= form_tag post_review_path, method: :get, :review_queue_id => #review_queue.id, :status => 'accepted'} ) do |f| %>
<div class='form-group'>
<label for='comment'>Please give a reason? (required)</label>
<%= text_area_tag(:reasons, placeholder: 'Your commentns ...', rows: 9, class: 'form-control') %>
</div>
<div class='modal-footer'>
<%= submit_tag 'Approve', class: 'btn btn-success btn-decission btn-modal-left-side' %>
<button type='button' class='btn btn-default' data-dismiss='modal'>Close</button>
</div>
<% end %>
And in the controller access it like params[:reasons]. Also if you noticed, I've added method: :get to the form_tag as you don't want to save the info to DB
The rails helper form_for is used for forms for rails resources. You want to use the form_tag helper. Search for form_for and form_tag here for more information on these 2 methods.

What is the correct button_to syntax for adding a class to the generated form?

I'm trying to apply a class to the form generated by button_to in Rails 3.
The :class option sets the class for the submit button so the docs tell us to use :form_class to apply a class to the form.
E.g.
<%= button_to 'x', user_contact_path(#user, contact), :method => :delete, :form_class => "delete" %>
This just adds the attribute form_class="delete" to the button element. I've tried various combinations using :html_options and so on.
Anybody know how to do this?
That method works perfectly fine for me. I am able to do:
<%= button_to "Hello", root_url, :method => :get, :form_class => "my_class" %>
the above generates the following:
<form action="http://localhost:3000/" class="my_class" method="get">
<div><input type="submit" value="Hello"></div>
</form>
However, this is in Rails 3.1 as the link in your question points and the same wouldn't work in Rails 3.0.x since the form class is hard coded.
From url_helper code:
("<form method=\"#{form_method}\" action=\"#{html_escape(url)}\"
#{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" +
method_tag + tag("input", html_options) + request_token_tag +
"</div></form>"
).html_safe
Try with
<%= button_to 'x', user_contact_path(#user, contact), {:method => :delete, :form_class => "delete"} %>
This forces :form_class => "delete" to be part of the options hash instead of the html_options hash.

Generate hidden form field data with rails button_to helper?

Currently, i use the following code for passing params[:id] to line_items create
<%= button_to 'Add to Cart', { controller: 'line_items', action: 'create', id: brick }, class: 'green radius nice button', method: :post, remote: true %>
However, it will append the id param on url query which i didn't suppose button_to to do. What i want is passing the id param via hidden the form field. Can i do that in button_to or any workaround for this issue?
Starting with Rails 4.1.0, button_to has now the additional options :params which can receive a hash that will be transformed into hidden fields. For example:
button_to 'Send Invitation', invitation_path(user), params: {'user[email]' => user.email}
Will give :
<form action="/users/invitation" class="button_to" method="post">
<div>
<input type="submit" value="Resend">
<input name="user[email]" type="hidden" value="user#yahoo.com">
</div>
</form>
I have done something similar in a module of my project:
First, I added a form with a hidden field:
<%= form_tag({:action => 'my_action'}, :id=>"item_form", :method => :post, :remote=>true) do%>
<%= hidden_field_tag :item_id, 0%>
<% end %>
all mi items on the list will have a check_box_tag wich contains the item id:
<%= check_box_tag "item_ids", item.id, false,:id => item.id, :class => "my_fancy_class"%>
when an item is picked just catch the event, replace the value on the hidden_field and trigger the form:
function( event, ui ) {
$("#item_id").attr('value',ui.selected.id);
$('form#item_form').submit();
}
if you want to render stuff just add a action.js.erb file to your controller view folder, but thats another thing.

Rails: How to change this link into a button?

Right now I can call a method using ajax (:remote=> 'true') at awisprotect_path by simply clicking on the "x" in this link
<%= link_to "x",
awisprotect_path,
:remote => true,
:method => :post,
%>
The controller action renders jquery so the response is included into the html in the view
<div class="awishanswer">
</div>
That's all working fine. However, instead of having an "x" to click, I wanted the user to click a button and get the same result. So I essentially just wanted to put the link info
<%= link_to "x",
awisprotect_path,
:remote => true,
:method => :post,
%>
into this button
<button class="btn small primary" >
check
</button>
So I created this form and put it in a partial
<%= form_tag(:controller => "sessions", :action => "awisprotect", :remote => true, :method => "post") do %>
<button type="submit" class="btn small secondary">check awis</button>
<% end %>
but the problem is that the controller action that renders js is not putting the result of the action into the html div. Instead, it's redirecting to a blank page and then printing the jquery method with the result that I was checking for with the controller action. the blank page just shows this...
$('div.awishanswer').html(' html to be inserted in div');
Can anyone explain?
In the url it says
http://localhost:3000/awisprotect?method=post&remote=true
in the view file
<div class="awishanswer" id="awishanswer">
<% form_remote_tag :url => {:controller => "sessions", :action => "awisprotect"},
:html => {:method => "post"}, :update => "awishanswer" do %>
<input type="submit" class="btn small primary" value="check" />
<% end %>
</div>
in the action
def awisprotect
#flag = params[:flag] // suppose sending parameter flag from form
// do something
render :partial => 'partial file containing html to send to view'
end
The form will be submitted when the submit button is clicked.
the action will send the html contained in partial file.
the form will update the div with id provided in form with the html code send back from action.
EDIT:partial file
<%if #flag%>
// include some html
<%else%>
// include some other html
<%end%>
The reason your getting a problem is probably because of your usage of the form_tag helper uses the :remote and :method values inside the url generation instead of being handled be the form. The correct usage would probably be like this:
<%= form_tag({:controller => "sessions", :action => "awisprotect"},
:remote => true,
:method => "post")
However, Rails already has a helper method to create a button to submit data called button_to. It basically takes the exact same arguments as the link_to helper so I would probably use it like this in your case:
<%= button_to "x", awisprotect_path, :remote => true, :method => :post %>
The :method argument could possibly even be left out because I think the button_to helper defaults to the POST protocol.
You can disguise a link as a button, using some CSS. Here's a nice article.
This might be better than all these experiments with partials and forms. :-)
I'm thinking you didn't wrap the options for form_tag properly. Try something like this:
form_tag( { :controller => :sessions, :action => :awisprotect, :method => post }, { :remote => true } ) do ....
It may or may not also help to use button_tag.

Rails, how do you submit a form with a text link?

I am trying to get this form to submit correctly. Here's what I have so far:
<% form_for(:user, :url => update_user_setting_path, :remote => true, :html => {:method => :post, :class => "search_form general_form"}) do |f| %>
and the button renders with this code:
<li><%= link_to raw("<span class='button approve'><span><span>SAVE</span></span></span>"), :action => 'create' %></li>
I am using action create, is this correct?
Here is the rendered form tag:
<form method="post" data-remote="true" class="search_form general_form" action="/settings/2/update_user" accept-charset="UTF-8">
What am I missing? Thanks for your help!
No, you are not using link_to properly. You need to use a submit tag to submit your form, not a link_to tag, for example:
<% form_for(:user, :url => update_user_setting_path, :remote => true, :html => {:method => :post, :class => "search_form general_form"}) do |f| %>
...
<li><%= f.submit "Save" %></li>
If you want to use a text link you'll have to have javascript submit the form. For example, if you are using jQuery you could do the following:
<%= link_to 'Save', "#", :onclick=>"$('.search_form').submit()" %>
I like Pan's solution but I prefer to use the ID of the form directly which you can get from the dom_id(obj). The form_for helper also uses dom_id(obj) to assign the form's ID. This way you aren't dependent on setting classes by hand or subject to accidentally submitting more than one form that share the same CSS class. It looks a little stranger but I usually have a custom FormBuilder anyway so I just add a generic link_to_submit method to encapsulate this:
<%= link_to 'Save', "#", :onclick => "$('##{dom_id(#user)}').submit()" %>
You don't need to use an id or a selector if you have jquery, you can simply do :
= link_to 'Save', "#", onclick: "$(this).closest('form').submit()"
Thanks for the answers... I ended up using this and it works great:
<li><%= link_to raw("<span class='button approve'><span><span>SAVE</span></span></span>"), "index_users", :onclick=>"document.forms['form1'].submit();"%></li>

Resources