I have todo list app. In the application there are multiple places wher you can mark a todo done by clicking in a checkbox.
This is the erb
<% #todos.each do |todo|%>
<%= form_for todo, remote: true do |f| %>
<%= f.check_box :completed, :class => "dashboard_done_box", :id => todo.id %>
<% end %>
<% end %>
and here's the js
$(document).on('click', ".dashboard_done_box", function(){
$("#edit_todo_" + $(this).attr('id')).submit()
});
The routes.rb has the line
resources :todos
When I refresh the page it works, but when I am navigating to it I get this error.
Started POST "/todos/1" for 127.0.0.1 at 2014-05-07 13:18:45 +0200
ActionController::RoutingError (No route matches [POST] "/todos/1"):
It also works when I disable turbolinks.
This code works on other pages in my application it is just on one page I have this problem.
When it works in makes a PATCH request
Started PATCH "/todos/1" for 127.0.0.1 at 2014-05-07 13:44:08 +0200
Processing by TodosController#update as JS
Why does it do a POST request and how is it connected to turbolinks?
Update
Yosep Kim suggested I should make a post route
routes.rb
post 'todos/:id' => 'todos#update', :as => :todo_update
The form
<%= form_for #todo, remote: true, url: todo_update_path(#todo), method: :post do |f| %>
Now I get trough to the controller, but only with these parameters
{"controller"=>"todos", "action"=>"update", "id"=>"3"}
Conclusion: The form submission is not getting serialized. Why?
How does the form render?
Rails uses the PATCH verb to update because in rails applications a update is almost always a partial update. The PUT verb should be used for complete updates ( like when you overwrite a file ) Read more here Riding With Rails
Rails uses hidden fields to accommodate the html verb.
Your form should render like this
<form accept-charset="UTF-8" action="/todos/5" class="edit_todo" data-remote="true" id="roster_edit_done_todo_5" method="post">
<div style="display:none">
<input name="utf8" type="hidden" value="✓">
<input name="_method" type="hidden" value="patch">
</div>
<!-- What ever fields you need -->
</form>
Be aware if you have the form in a table. The table might make the form render strange.
There is a bug on latest 64bit Chrome dev/beta channel version 37,38.
If you use it, see https://code.google.com/p/chromium/issues/detail?id=388664
First of all, your code is creating two redundant requests, when the checkbox is clicked. The first one is fired by the form, and the second by the JS code you have. The one that is fired by the form would ONLY work one time after the page refreshes. Once you click on it, Turbolink would refresh the contents in the BODY without reloading the DOM, failing to attach the AJAX event handler to the form.
You can suppress the default behavior by using preventDefault. This should only fire the action once through the JS code.
$(document).on('click', ".dashboard_done_box", function(e){
e.preventDefault();
$("#edit_todo_" + $(this).attr('id')).submit()
});
Related
Whenever I submit a form rails append a unknown fragment in params in the url. But what is the purpose of that fragment ? and how I can get rid of it? Please refer to following sample URL.
By token I am referring to "#.U3Mw4XKHbFY" in the following URL
Here is URL sample
www.domain.com/posts?utf8=%E2%9C%93&query=surf&area=All+Area#.U3Mw4XKHbFY
<%= form_tag posts_path, method: 'get', class: "search_keywords_form" do %>
<%= text_field_tag :query, nil, class: "search-field", placeholder: "Search Item for swap" %>
<%= hidden_field_tag 'area', "All Area" %>
<input type="submit" class="search-btn" value="" />
<div class="clearfix"></div>
<div class="push"></div>
<% end %>
Another strange thing is when I review the params, I don't get it there. But when page get reloaded That unknown attribute get appended.
params = { "utf8"=>"✓",
"query"=>"surf",
"area"=>"All Area",
"action"=>"index",
"controller"=>"posts"
}
I also had to face this problem, this is definitely JS problem might be some plugin would be appending this.
Simple solution for this is just put location.hash = "" in your js file on document ready. And yes i remembered, this fragment is appended when you use some tracking system. Like google analytics or Addthis
What are you talking about is the fragment part of an URI. The fragment part of the URI refers to some part of the HTML content response, and it is processed only client side: that's why you don't - and you can't - see or process it server side.
The fragment appears because it is present somewhere in the HTML form, or because some client side code (aka JavaScript) sets it to the form URL.
I have the feeling I have already seen that fragment pattern somewhere, but I don't remember where...
how to set method inside form to put in form_tag in rails?, i have form_tag like this :
<%= form_tag(url, :method => :put, :multipart => true, :class =>"form-horizontal") do %>
......
<% end %>
but if i inspect element, form not have method "put" but still "post"?
<form accept-charset="UTF-8" action="/admin/stores/1/information/social_update" class="form-horizontal" enctype="multipart/form-data" method="post">
....
</form>
why???
According to the docs:
If “patch”, “put”, “delete”, or another verb is used, a hidden input with name _method is added to simulate the verb over post.
Therefore, the following code will output the following markup:
form_tag('/posts/1', method: :put)
#=> <form action="/posts/1" method="post"> ... <input name="_method" type="hidden" value="put" /> ...
This is basically a backwards compatible way of implementing the PUT protocol across browsers. Although the form's submission method is POST, because of the hidden form input, Rails understands the desired submission method to be PUT.
The Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PATCH" and "DELETE" requests (besides "GET" and "POST"). However, most browsers don't support methods other than "GET" and "POST" when it comes to submitting forms.
So, it's perfectly ok to have POST instead of PUT.
Reference
Try entering
:method => 'get'
That should do the trick and display get when you inspect the element.
Added on edit, 2013-02-11:
I should make it clearer that the problem I'm having is not that I can't get the ERB code I write to produce correct HTML code. Rather, it is that my Rails installation sometimes interprets the HTML code correctly, so that clicking the link has the right effect, and sometimes incorrectly, producing a "No route" error. More details in my answer below.
Original question, 2013-02-08:
I'm making a simple login form using Rails 3.2.11. I've tried several ways of coding the Submit button of the form, but each has failed due to a missing route error. I got one method to work, and checked in my code. However, now the very same code fails.
This is the code of app/views/session/new.html.erb (I'm using simple_form, but not its button wrapper):
<h2>Log in</h2>
<%= simple_form_for :session do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit", session_index_path %>
<% end %>
The relevant part of the HTML code that this produces is:
<h2>Log in</h2>
<form accept-charset="UTF-8" action="/session/new" class="simple_form session" method="post" novalidate="novalidate">
<div style="margin:0;padding:0;display:inline">...</div>
...the input fields...
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit" />
<input name="authenticity_token" type="hidden" value="...token value here..." />
</div>
</form>
</form>
This is my config/routes.rb:
MyApp::Application.routes.draw do
resources :at_user
resources :session, :only => [:new, :create, :destroy]
match 'login' => 'session#new', as: :login
match 'logout' => 'session#destroy', as: :logout
root to: 'main#index'
end
This is what the command rake routes outputs:
at_user_index GET /at_user(.:format) at_user#index
POST /at_user(.:format) at_user#create
new_at_user GET /at_user/new(.:format) at_user#new
edit_at_user GET /at_user/:id/edit(.:format) at_user#edit
at_user GET /at_user/:id(.:format) at_user#show
PUT /at_user/:id(.:format) at_user#update
DELETE /at_user/:id(.:format) at_user#destroy
session_index POST /session(.:format) session#create
new_session GET /session/new(.:format) session#new
session DELETE /session/:id(.:format) session#destroy
login /login(.:format) session#new
logout /logout(.:format) session#destroy
root / main#index
The target path of button_to is session_index_path, which should cause the create method of SessionController to be called -- and for a while, it did. Now, after I've restarted Rails, pressing the button instead produces an error page, with the text
No route matches [POST] "/session/new"
For some reason, Rails has started to think that the target of button_to is session#new instead of session#create. It's as if it thinks that the HTTP method it's supposed to call is GET instead of POST -- however, the HTML code shows that the method is post.
By the way, another thing I earlier tried was to give button_to the action and method parameters, as documented here:
<%= button_to "Submit", options: {action: 'create', method: :post} %>
then this is what's generated:
<form action="/session/new?options%5Baction%5D=create&options%5Bmethod%5D=post" class="button_to" method="post">
which doesn't look like what I want, either. button_to's default HTTP method is POST, which appears in the result, but the :options hash is just tacked onto the end of the URL, and the word create appears nowhere else.
The first answer to this question says that, unlike what the documentation says, one should not put the parameters of button_to in a hash, but give them directly. So, I tried this:
<%= button_to "Submit", action: 'create' %>
However, the action create still does not show up in the generated HTML:
<form action="/session" class="button_to" method="post">
So, those were things I tried before attempting to using the named path method, which worked for a minute, but for some mysterious reason, doesn't anymore.
Any ideas what I'm doing wrong? (It's probably something obvious I've overlooked.)
The button_to documentation is actually wrong in a few places (Note my answer here: Why is this button_to rendering incorrectly in Rails 3.2.11?). To fix that Submit button, use this:
<%= button_to "Submit", { action: 'create' }, method: :post %>
The options and html_options hashes need to have explicit separations defined using the {} braces depending on what you are trying to accomplish.
The create action won't show up in the URL since a POST HTTP method to /session is routed to the create action.
So far, the method suggested by Cyle in a comment above seems to work best, namely, adding url: session_index_path to the simple_form_for declaration:
<h2>Log in</h2>
<%= simple_form_for :session, url: session_index_path do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit" %>
<% end %>
I haven't yet experienced a route error when clicking the "Submit" button produced by this code.
I'm not accepting this answer, though, because the base problem, which I haven't solved yet, is the fickleness of Rails; that is, code that has worked before suddenly stops working. For instance, today I first tried this code:
<%= simple_form_for :session do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit1" %>
<%= button_to "Submit2", session_index_path %>
<% end %>
Pressing the "Submit2" button worked. However, when I removed the line that contained button "Submit1", stopped and restarted the Rails server (as I've done every time between attempts), and reloaded the page, pressing the Submit2 button produced a "No route" error, even though the HTML code of the button remained the same:
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit2" />
<input name="authenticity_token" type="hidden" value="...this value was the same also..." />
</div>
</form>
Very puzzling. Might be related to the details of my setup, though it isn't that exotic.
Added on edit:
When I took look at the relevant part of the HTML that is produced by Cyle's method, I noticed that it's the same as for almost all the other cases:
<form accept-charset="UTF-8" action="/session" class="simple_form session" method="post" novalidate="novalidate">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" ... />
</div>
...the username and password input fields...
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit" />
<input name="authenticity_token" type="hidden" value="voOGIAbJfwvjxVRbk02V5l6zn6iwvOiMvaeauPBIrqU=" />
</div>
</form>
So it might be just luck that this has worked for me so far.
The problem was most probably that after I created the application, I decided to change the names of a couple of the classes and the associated files. This resulted in class and file names that didn't follow the Rails convention on pluralizing nouns in certain contexts. For instance, I had a file session_controller.rb that contained a class named SessionController; the correct names are sessions_controller.rb and SessionsController.
I created a dummy Rails application, and then commanded rails generate scaffold AtUser and rails generate scaffold Session, and then used the resulting filenames and identifiers as a guide for correcting the actual application. Now, I don't get those "No route" errors anymore. The command rake routes outputs:
at_users GET /at_users(.:format) at_users#index
POST /at_users(.:format) at_users#create
new_at_user GET /at_users/new(.:format) at_users#new
edit_at_user GET /at_users/:id/edit(.:format) at_users#edit
at_user GET /at_users/:id(.:format) at_users#show
PUT /at_users/:id(.:format) at_users#update
DELETE /at_users/:id(.:format) at_users#destroy
sessions POST /sessions(.:format) sessions#create
new_session GET /sessions/new(.:format) sessions#new
session DELETE /sessions/:id(.:format) sessions#destroy
login /login(.:format) sessions#new
logout /logout(.:format) sessions#destroy
root / main#index
Before this, I saw a "No route" error also when I included a file in another file without having required the former at the top of the latter. After performing the renaming fix, I tried to reproduce this error, and it did reoccur: Rails mistakenly reports a missing route, when the actual problem is a missing require statement. This suggests that Rails's error reporting mechanism is buggy.
Thanks to user bullfrog for pointing me in the right direction.
I have a Rails app with a Bootstrap modal.
Here is part of the code that launches the modal:
<a type="button" class="btn" href="#labor_modal" data-toggle="modal" data-woid= <%= workorder.id %>><i class="icon-time"></i></a>
Here's part of the code for the modal:
<div class="modal fade" id="labor_modal">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>Add Labor</h3>
</div>
<div class="modal-body">
<%= simple_form_for(Event.new, validate: true, remote: true, html: {"data-type" => :json}) do |f| %>
<%= f.hidden_field :workorder_id, :value => data-woid.val() %>
I want to get the work order.id passed to the modal so it can be saved in the new record.
This code isn't working:
<%= f.hidden_field :workorder_id, :value => data-woid.val()
Any help would be appreciated !!
PS - I was hoping I could do this without jquery - but, if I need it so be it.
<%= %> is an ERb (embedded Ruby) tag. Inside should be Ruby code.
data-woid.val()
is not valid Ruby in the context which you use it.
ERb is processed before rendering HTML. At the time ERb is processed, Ruby is not aware of the DOM or rendered HTML elements or their attributes. There is no way to read HTML data attributes in Ruby via ERb. You must access them after the page is rendered and the DOM is built. Use JavaScript/jQuery for this.
I'm guessing that work_order.id will be used to set an association. Exposing this to the user is insecure; it is possible to change the value to anything before submitting. Associations should be set in the controller, specifically the create action before you call save.
Well the question said without javascript. My answer might not work in every scenario or answer the question perfectly, plus it is will not scale, but it uses no javascript!
So what I would do is create as many modals as I need according to the parameter. I would do that by assigning them different ids. This can be done dynamically on rails.
Then each link would open (reveal) its matching modal, and it would seem like a parameter was sent, because each modal would have the necessary information, set at render time.
thanks for reading this post. I've been stuck on an issue with RoR for the past few days. I have a form under index.html.erb as:
<head>
<title>Ajax List Demo</title>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
</head>
<body>
<h3>Add to list using Ajax</h3>
<% form_tag :action => :list , :method=>:get, :remote=>true do %>
Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
<%= submit_tag "Find" %>
<% end %>
<div id="my_list">
</div>
</body>
In the controller I have:
def list
puts "here!!!!"
reader = Reader.new
#profiles = reader.processURL(params[:url]) #profileList =
respond_to do |format|
#format.html { render :partial=>true, :locals => { :profiles => #profiles}}#{ render :partial=>'profiles/list',:layout => false, :locals => { :profiles => #profiles}}
format.js {render :content_type => 'text/javascript', :locals => { :profiles => #profiles}}
# index.html.erb
# format.rss render :partial=>'profiles/list',:layout => false, :locals => { :profiles => #profiles}
end
And a js file for remote UJS as list.js.erb
$("#my_list").html("<%= escape_javascript(render(:partial => "list"))%>");
The issue is I cannot get the results to render the partial _list.html.erb, in the div tag my_list. I get a blank page, 406 error. If I un-comment the render html code in the controller I get the partial back rendered in the browser. I am kind of stuck, I want to submit the form and the results to pop in the my_list div. I'm new to rails so if I'm missing something obvious don't hesitate to point it out to me....I'm definitely willing to try.
Changed it to this:
<html>
<head>
<title>Ajax List Demo</title>
<h1>Listing posts</h1>
<%= javascript_include_tag 'jquery.js' %>
<%= csrf_meta_tag %>
</head>
<body>
<h3>Add to list using Ajax</h3>
<% form_tag :action => :doit , :method=>:get, :remote=>true do %>
Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
<%= submit_tag "Find" %>
<% end %>
<div id="my_list">
</div>
Controller:
def doit
puts "here!!!!"
reader = Reader.new
#profiles = reader.processURL(params[:url])
respond_to do |format|
# format.html {render :partial=>true, :locals => { :profiles => #profiles}}#{ render :partial=>'profiles/list',:layout => false, :locals => { :profiles => #profiles}}
format.js #{render :content_type => 'text/javascript', :locals => { :profiles => #profiles}}
# index.html.erb
# format.rss render :partial=>'profiles/list',:layout => false, :locals => { :profiles => #profiles}
end
JS
_doit.js.erb
$("#my_list").html("<%= escape_javascript(render(:partial => "doit"))%>");
And finally a partial:
_doit.html.erb.
However I am still getting the 406 error, I dont have a duplicate _doit js or erb. Does anything standout as incorrect from this? Thanks again!
Another update:
I think the form is not rendered correctly:
This rendered:
<% form_tag :action => :doit , :remote=>true, :id => 'myform' do %>
Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
<%= submit_tag "Find" %>
<% end %>
This:
<form accept-charset="UTF-8" action="/home/doit?id=myform&remote=true" method="post">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="MLuau4hvfdGO6FrYCzE0c0JzwHhHKZqjmV49U673sK8=" />
</div> Enter the public url:
<input id="url" name="url" size="80" type="text" value="" />
<input name="commit" type="submit" value="Find" />
<input name="commit" type="submit" value="Find" />
Its adding my remote tag and id to the query string, isnt this wrong?
Ok finally got a clue forms need to be bracketed:
<%= form_tag( { :action => 'doit' }, :multipart => true, :remote=>true, :id => 'myform' ) do %>
Ok last update tonight:
Now I get in the logs:
Started POST "/home/doit" for 127.0.0.1 at Wed Oct 27 22:40:55 -0400 2010
here!!!!
Processing by HomeController#doit as JS
Parameters: {"commit"=>"Find", "url"=>"http://www.facebook.com/people/James-Stewart/653161299", "authenticity_token"=>"MLuau4hvf
dGO6FrYCzE0c0JzwHhHKZqjmV49U673sK8=", "utf8"=>"Γ£ô"}
Rendered home/_doit.html.erb (4.0ms)
Rendered home/doit.js.erb (9.0ms)
Completed 200 OK in 807ms (Views: 40.0ms | ActiveRecord: 0.0ms)
I see as JS and it says it renders my js/partial. However I am getting nothing on my_list div. My JS file:
$("#my_list").html("<%= escape_javascript(render(:partial => "doit"))%>");
My html.erb form file has now:
<script$('#myform').bind('ajax:success', function(evt, data, status, xhr){
xhr.responseText;
});></script>
Its like the form does nothing, which is a good sign, no more 406 error. I know this is close, if anyone can point what I need to do in the js that would be great otherwise I'll take a break and try tmrw.
Ok I think its getting a response back just not rendering as you pointed out would be the issue yesterday Steve.
Debugging the JS on Firebug I see the html I want rendered in the div, for this:
http://localhost:3000/javascripts/prototype.js?1285674435/event/seq/1
Which means I think I am getting the JS response back now.
I have this on the form page:
<script>$('#myform').bind('ajax:success', function(evt, data, status, xhr){
$('#my_list').html(eval(xhr.responseText));
});</script>
Inspections say it doesnt know what myform is, but I put :id => 'myform' in the Rails code.
Again all thanks, I got a ton of help here and I want to share how I finally got it working back to the community.
The, js file for the method doit(def. need a better controller action name) is doit.js
The code was ultimately:
$("my_list").update("<%= escape_javascript(render(:partial => "doit"))%>");
For some reason leaving it as #my_list wouldn't be found in firefox, I had to use firebug to finally figure this out.
Obviously this is different from the way suggested below, and I am going to place the js script back into the form and remove the .js.erb file and see how that works works. I suppose I just render the partial in the format.js response? Also where does everyone find info on writing the UJS files? I know nothing about the syntax for anything starting with $.
Again thanks for the help, finally feel like I am making progress on learning rails.
I posted this answer on Hacker News, but figured the Stack Overflow community might benefit as well :-)
In Rails 3, the javascript drivers are very hands-off (i.e. unobtrusive). The problem you're having is that your app is returning to the browser a string of javascript, but there is nothing in the page that is then executing that javascript in the context of the page.
The rails.js ujs driver binds to forms and links with data-remote=true, which is what the :remote => true is doing, to make them submit their requests remotely, but that is where the Rails magic stops.
The good news is that the remote requests fires off some events you can bind to, which give you access to the data returned by the server (which fire off in the following order):
ajax:before
ajax:loading
ajax:success
ajax:complete
ajax:failure
ajax:after
You need to bind an event to the ajax:success event of your form. So, if your form had the id "myform", you'd want something like this on your page:
$('#myform').bind('ajax:success', function(evt, data, status, xhr){
eval(xhr.responseText);
});
xhr.responseText is what your server returns, so this simply executes it as javascript.
Of course, it's proper to also bind to the failure event with some error handling as well.
I usually don't even use the action.js.erb method of returning javascript, I just have my controller render the HTML partial, and then I have a binding like this in the page:
$('#myform').bind('ajax:success', function(evt, data, status, xhr){
$('#target-div').html(xhr.responseText);
});
I'm actually in the middle of writing a full article on this, but hopefully this is enough to get you going.
EDIT: I finished that article, fully explaining remote links and forms in Rails 3. Maybe it will help:
Rails 3 Remote Links and Forms:
A Definitive Guide
If you look at the rendered source of your page, you should notice an error in the fields attributes.
The correct way to do it is as follows:
<%= form_tag({:action => 'list'}, :remote => true %>
Notice the curly brackets, very important! Alternatively you could have done:
<%= form_tag('list', :remote => true %>
Do you have 2 partials named '_list'? Maybe that's causing problems and you should just a little more specific:
$("#my_list").html("<%= escape_javascript(render(:partial => "list.html.erb"))%>");
I'm not sure if this helps, but are if you using in IE be aware that IE sends some headers that screw with how your controller responds. So you may be sending an Ajax request with IE, but your Rails app thinks its just a plain html request.
I've had to setup jQuery to first erase the current headers and then add just the javascript header:
$.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept",'');xhr.setRequestHeader("Accept", "text/javascript")}
})
Using list as your function name in the controller may be the problem. That name is used internally by Rails.
Rails 5.1 introduced rails-ujs, which changes the parameters of these event handlers. The following should work:
$('#myform').bind('ajax:success', function(event) {
const [_data, _status, xhr] = event.detail;
});
Source:https://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html#rails-ujs-event-handlers
I know this is an old question but someone can benefit from it.
I think the error is related to this:
JS _doit.js.erb $("#my_list").html("<%= escape_javascript(render(:partial => "doit"))%>");
And finally a partial:
_doit.html.erb.
You are creating a _doit.js.erb partial to respond to the action doit in the controller but what you need is a view called doit.js.erb (without the underscore). Conceptually, the format.js in your action will respond to a view with the same name of it with extension js.erb.