Pass js variable to server code - ruby-on-rails

I have a form with 2 inputs and button. An user put feed url in the first input and press the button:
<%= link_to "get name", { :controller => 'Feeds', :action => "get_title" },
:remote => true, :class=>'btn btn-mini' %>
Here is controller method
def get_title
respond_to do | format |
format.js {render :layout => false}
end
end
And here is a get_title.js.erb:
var url = $( "#feed_url" ).val();
console.log(url);
$( "#feed_name" ).val("<%= #proxy.title(url) %>");
I get value of the first input and want to pass it as parameter to Ruby class. But in this case I get the error:
ActionView::Template::Error (undefined local variable or method `url' for #<#<Class:0x41ec170>:0x41ef968>):
1: var url = $( "#feed_url" ).val();
2: console.log(url);
3: $( "#feed_name" ).val("<%= #proxy.title(url) %>");
Rails this that 'url' is a Ruby variable, but not JS one.
How can I pass JS variable to Ruby code ?
Thanks in advance.

Remember that any ERB (ruby) code is executed server side, while Javascript is, of course, rendered client side. As a result, your line <%= #proxy.title(url) %> is rendered WAY before that url value is ever evaluated. The solution to your situation is more along the lines of passing data to Rails, and rendering the response. Three things to facilitate this (bearing in mind that this is only one approach, and I'm sure there are plenty of others, and possibly better ways of doing this):
1- Your link_to won't post the user-input URL value because it is not POSTing the form. Instead, change the surrounding form to use :remote=true, and use a typical form.submit button rather than this link. Your form with the URL value will be submitted (and it will be asynchronous).
2- In your controller, render your title like you were trying to do, doing something along these lines:
def get_title
render :text=>#proxy.title(params[:url])
end
3- Bind to the ajax:success event, something along these lines:
$("form#myForm").bind("ajax:success", function(event, data, status, xhr){
$( "#feed_name" ).val(data) // data, in this case, is the rendered `#proxy.title(url)` we did in step 2.
})
Hope that makes sense. Let me know if it does not.

Related

how to send remote form params

I have a form which is using several input fields being ajaxified with remote: true and custom controller actions. This way UJS driver sends only given form input as params. What I want to accomplish is an ajaxified input field which will send value of a field outside of it's "scope" as params. What do you suggest?
Let me explain the whole flow of the ajax-rails and remote=> true and you can do this with that following way and you can get all the params in your method.
first when you used the remote=> true then the form will submit or call the action which you have define in form like here is an example:
<%= form_tag({:controller => 'my', :action => 'my_data'},:id => 'filter_form', :remote => true) do %>
#code here
<%= submit_tag 'save', :name => 'commit'%>
<%end%>
now above code will go to my_data action in my controller,
here you can define the respone type with
def my_data
#actions on data here
respond_to do |format|
format.js
end
now you have to made a .js file with named as action name....
my_data.js.erb
here the whole affect of form you can write and update document element through jquery and javascript.
For every input you just assign a class and write a method that call when the input box will change.One more thing that when you wants to do in a single method the every input have a unique ID, send that also in params and do task what ever you wants to do.
Rails' JQuery UJS is just a dependency which Rails uses to call ajax requests on your behalf. This means your question is not particularly to do with JQuery UJS, but with how to handle it with ajax overall
You'll typically handle any extra data by appending it to the Ajax request:
<%= link_to "Link", new_post_path(params: "here"), remote: true %>
If you want to create an "ajaxified" field, you'll probably have to use a manual ajax call to capture the change event, allowing you to send the data to your event:
#app/assets/javascripts/application.js
$('input[name="element"]').on("change", function(){
$.ajax({
url: "url",
data: $(this).serialize()
});
});
Then, as mentioned by #baharat Soni, you'll be able to handle the request on the server using respond_to

best_in_place - use updated value back in view

I have an idea and a problem I can't seem to find answer or solution to.
Some info if required later or helpful:
Rails version 3.2.9
best_in_place (git://github.com/straydogstudio/best_in_place.git)
Ruby 1.9.3p327
Ok, so i have settings page where i can update individual setting by editing them with use of best_in_place gem. Works fine. Happy with that.
Some of the settings are interconnected, meaning, i have to sum or subtract them.
As a helpful tip for the user, in my view, right beside the in place form for that settings there is also a calculated value.
Now, of course, I would like to see this value be update along with the attribute itself.
I can't find a way to do that.
If i do it with the :data => it works, but i get the old and not the new value, so my view is always "1 step behind".
i have also tried with update.js.erb and _test.html.erb partial, but javascript file doesn't work. It is like it doesn't exist. I have double, triple checked where to put it and it is OK (app/views/controller/_partial.html.erb)
So. Pretty straightforward question would be; how can i access an updated value and use it back in view to update calculations. I personally think I should go with the partial and js file - but I have no clue why JS file isn't picked up. Any idea on that?
If there are any other options, i would more than appreciate the hint.
Thanks!
--EDIT (code added)
view:
<td>
<%= best_in_place #s,:pay_after_bonus, :display_with => :number_to_percentage, :type => :input, :nil => "Klikni tu za spremembo!", :cancel_button=> "Prekliči" %>
Cena: <span id="test"><%= number_to_currency(#s.family_member_price - ((#s.pay_after_bonus * #s.family_member_price)/100)) %></span>
</td>
settings_controller.rb:
def update
#s = Setting.find(params[:id])
respond_to do |format|
#s.update_attributes params[:setting]
#s.create_activity :update, :params => { :setting => params[:setting].keys.first }, owner: current_user
format.json { respond_with_bip(#s) }
format.js
end
end
update.js.erb:
$( "#test" ).html( "<%= escape_javascript( render( :partial => "update") ) %>" );
_update.html.erb:
<strong>Test</strong>
-- EDIT 2:
OK, apparently it is possible to do something like I want this way:
$('.best_in_place[data-attribute="model_attribute"]').bind(
"ajax:success", function(event, data) {
// function that will update whatever
});
in combination with
// respond to json request with
render :json => {"model" => #model}
&
"ajax:success", function(event, data) {
var result = $.parseJSON(data);
// from here the result var will be accessible with all the data returned by the controller.
// result.model is your object - use result.model.attribute to get specific values...
}
But here it ends for me.
I don't know how to use render :json => {"model" => #model} in my case, as it has to be done in combination with format.json { respond_with_bip(#s) }.
Where do I put render in controller?
Currently I get 500 internal server errors trying to do this as a response.
I have found this solution here.
Thanks!!
In the ajax callback you can make another request to get the partial you want:
$('.best_in_place.myclass').bind("ajax:success", function () {
$.getScript("http://www.someurl.com");
});
Then in the action you render a JS file (eg: show.js.erb), where you replace the target element with the results of the render:
$("#div-<%= #div.id %>").replaceWith("<%= escape_javascript(render(partial: 'some/partial', :layout => false)) %>");
You can use jQuery to parse the dom for the element that best in place just changed, and get the updated value from there. For example, of you have this code (haml)
= best_in_place #user, :name, :classes => 'name-edit'
Then your callback would look like this (coffeescript)
$('.name-edit').on 'ajax:success', (event, data, status, xhr) ->
newValue = $('.name-edit').html # could also use .attr() here
$('.some-other-widget').html(newValue) # again could also set .attr here
You can even skip looking up the new value with jQuery. In the context of the callback handler, 'this' represents the element the call was made from, so you could just do
$('.name-edit').on 'ajax:success', (event, data, status, xhr) ->
$('.some-other-widget').html this.innerHTML
and get the new value to the other widget that way. My guess is the event returned by the ajax handler also has currentTarget, which again would be the widget that trigged the ajax request. My only worry on all this would be that your success handler somehow beats the best in place handler, and you get the widget before it's updated. In my testing that hasn't ever happened.
I just answered a question like that:
Answer here
but instead of just inserting the value you need to use the sum that you need.

How can I add content dynamically in rails?

I have controller with a page named list.html.erb
when a user clicks a link I want to append content dynamically to the page and execute javascript afterwards.
is it possible to have a method which renders a js.erb file and I put into this file the variables containing the content?
best phil
As described there: http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to you need to use
link_to 'Something', 'somewhere', remote: true
Yes, of course. It may not be the best course of action, though.
If you're enhancing the functionality of the view through Javascript, it's probably better to keep the code in Javascript rather than Ruby-generated Javascript.
Make a simple HTML link that GETs the new content from a controller in the JSON format or even as an HTML snippet. Use something like $.ajax and its success callback function to append said response to the element in the page.
$.ajax({
url: "whatever/here",
type: "GET",
data_type: "json",
success: function(response) {
$("#element_in_question").append(response); //if HTML; if JSON, parse and build the HTML
},
error: function(xhr, status, message) {
//indicate failure somehow
}
});
You can include a partial in your list.html.erb and display the partial on changing some content on the page list.html.erb. For example considering your list.html.erb file, you can include the following in your page
<form_for :something, :remote => true, :method => get :url => {:action => "partial"} do |f| %>
//write your code here
<%=f.submit%> //here you can have :onchange in the form_for tag if you do not want a submit button
write a division for partial in your list page as below
<div id="partial">
</div>
write a partial.js.erb file like this:
$('#partial').html("<%=j render "partial" %>");
create a _partial.html.erb file with the content you want to add the list.html.erb dynamically.
in the controller, add the following code:
def partial
//write your code here
respond_to do |format|
format.js
end
This will display the added content in the div tag of your list page with div id "partial"

Rails3 Update Boolean Checkbox from Index View

I'm building a simple tasks application for our company as part of an ordering system.
I have a list of tasks with a number of rules. Nothing complex... What I'm stuck on is the addition of a checkbox to complete the task. I want it done live, from the index view without having to hit submit..
Am really not sure even where to look. I figure I need to use ajax to do this - can anyone recommend a tutorial or tell me what I should be looking for.
Have also thought about a plugin, like the edit in place ones out there.
Thanks in advance
--- EDIT 1 --
Following advice from #pcg79 below, I've added the following to my application but am not understanding how I go out actually changing the status.
In my index view I have this:
<%= check_box_tag 'complete_task_1', '', false, { 'data-href' => tasks_path(#task) } %><
I've added the following to my application.js (added a # to get it to call properly)
$('#complete_task_1').click(function() {
$.ajax({
url: $(this).data('href'),
type: 'PUT',
dataType: 'html',
success: function(data, textStatus, jqXHR) {
// Do something here like set a flash msg
}
});
});
For lack of understanding, I added this to my tasks controller:
def completed
#task = Task.find(params[:id])
#task.status = true
end
Which seemed reasonable but wasn't sure how to actually call that in the ajax?
In my development log I can see it sort of working but it says this:
ActionController::RoutingError (No route matches "/tasks"):
-- EDIT 2 --
As per advice from #jdc below, I've tried adding the following to routes.rb:
get 'tasks/:id/completed' => 'tasks#completed', :as => :completed_task
But still get the RoutingError.
-- Slight Update --
Following the excellent advise from #pcg79 below, I've updated my files with the following.
Routes.rb
get 'task/:id' => 'tasks#completed', :as => :completed_task
Index.html.erb
<td><%= check_box_tag 'complete_task_1', '', false, { 'data-href' => completed_task_path(:id => task.id) } %></td>
Tasks controller
def completed
#task = Task.find(params[:id])
#task.status = true
#task.save
end
I get no errors in my browser, but my development log shows this:
ActionController::RoutingError (No route matches "/tasks"):
For a simple checkbox, this is hard work!!!
-- Another update --
Having played all day, I decided to see what would happen with a button_to instead, forgetting the ajax side of things. I put this in my code:
<%= button_to "Complete", completed_task_path(task.id) %>
And changed routes to:
match 'tasks/:id/completed' => 'tasks#completed', :as => :completed_task
Which worked a treat. Changing back to check_box_tag breaks it all again :(
Pretty much worked out it's the contents of my function. Having removed some code, I can update the css for a #:
$('#complete_task_1').click(function() {
$.ajax({
success: function(data, textStatus, jqXHR) {
$('#thing').css("color","red");
}
});
});
Any idea what I'd need to call my action?? J
If I understand what you're looking for (when the checkbox is checked or unchecked an Ajax request is sent to the server and the associated object is saved with the result of the checkbox), then yes you'll want to do it in Ajax.
With Rails 3 you're probably using jQuery (or, IMO, you should be). You'll need to implement a click event on the checkbox element. That click event, when it's fired, will do an Ajax call to your server. You'll want to do a PUT request since it's an update. You'll send the id of the object and the value of the checkbox.
There are a decent amount of sites that have examples of Rails and Ajax. This one (http://net.tutsplus.com/tutorials/javascript-ajax/using-unobtrusive-javascript-and-ajax-with-rails-3/) is good as it has you use the HTML 5 "data" fields which I like. There's also a bunch of similar questions here on SO. Here's one that's not Rails but will give you an idea of how to write the jQuery (AJAX Checkboxes).
Edit to answer question in comment
The checkbox can be wherever you want it since you're doing Ajax. If you want it on your index view, that's where you put it. Your checkbox will look something like this (please understand I'm not double checking my syntax or anything):
= check_box_tag 'complete_task_1', '', false, { 'data-href' => task_path(#task_1) }
Then your jQuery will look something like:
$('complete_task_1').click(function() {
$.ajax({
url: $(this).data('href'),
type: 'PUT',
dataType: 'html',
success: function(data, textStatus, jqXHR) {
// Do something here like set a flash msg
}
});
});
Second edit: I realized I forgot to actually send the value of the checkbox in the Ajax. You can do that and just call #task.update_attributes or you can make the url a specific method that only completes tasks.
Edit for updated question:
To explain my second edit, in order to update the task to be completed, you can do one of two things. You can either call a method that is expressly for setting the status attribute. Or you can call your normal, RESTful update method passing in :task => {:status => true} and call #task.update_attributes(params[:task]). You've chosen to do the former which, IMO, is fine.
So you have two problems. The first is that you aren't referencing the new route which points to your completed method. The second is that you aren't saving your object in the completed method.
To fix the first problem, you need to change the path your data-href attribute in the check_box_tag method points to. You don't want task_path. IIRC, you'll want completed_task_path(#task). The easiest way to find out the name of the path is to run rake routes in your Rails project's root directory.
To fix the second problem, just make sure to call #task.save at the end.
def completed
#task = Task.find(params[:id])
#task.status = true
#task.save
end
In your updated example, try replacing:
<%= check_box_tag 'complete_task_1', '', false, { 'data-href' => tasks_path(#task) } %>
with:
<%= check_box_tag 'complete_task_1', '', false, { 'data-href' => task_path(#task) } %>
Provided #task.id = 1, tasks_path(#task) returns /tasks.1, while task_path(#task) returns /tasks/1

Rails controller not rendering correct view when form is force-submitted by Javascript

I'm using Rails with jQuery, and I'm working on a page for a simple site that prints each record to a table. The only editable field for each record is a checkbox. My goal is that every time a checkbox is changed, an ajax request updates that boolean attribute for the record (i.e., no submit button).
My view code:
<td>
<% form_remote_tag :url => admin_update_path, :html => { :id => "form#{lead.id}" } do %>
<%= hidden_field :lead, :id, :value => lead.id %>
<%= check_box :lead, :contacted, :id => "checkbox"+lead.id.to_s, :checked => lead.contacted, :onchange => "$('#form#{lead.id}').submit();" %>
<% end %>
</td>
In my routes.rb, admin_update_path is defined by
map.admin_update 'update', :controller => "admin", :action => "update", :method => :post
I also have an RJS template to render back an update. The contents of this file is currently just for testing (I just wanted to see if it worked, this will not be the ultimate functionality on a successful save)...
page << "$('#checkbox#{#lead.id}').hide();"
When clicked, the ajax request is successfully sent, with the correct params, and the action on the controller can retrieve the record and update it just fine. The problem is that it doesn't send back the JS; it changes the page in the browser and renders the generated Javascript as plain text rather than executing it in-place.
Rails does some behind-the-scenes stuff to figure out if the incoming request is an ajax call, and I can't figure out why it's interpreting the incoming request as a regular web request as opposed to an ajax request.
I may be missing something extremely simple here, but I've kind-of burned myself out looking so I thought I'd ask for another pair of eyes. Thanks in advance for any info!
In your controller you need to specify the proper response. Since you didn't post the controller I'll just try to fill in the blanks.
def update
# Update something
respond_to do |format|
format.js # this renders your rjs file
end
end
Specifying the format tells the rails app to interpret the javascript instead of just sending it back as text.
The other option instead of using rjs is to do an inline rjs block like this:
render :update do |page|
page.replace_html 'user_list', :partial => 'user', :collection => #users
page.visual_effect :highlight, 'user_list'
end
Only use the inline rjs if you will be doing minimal changes to the interface that can be put into one or two lines. Anything more should be in it's own rjs file.
This question is related to this one, but the answer varies slightly. I had to create a new way to submit the form, since the default jQuery submit() method does not submit as a 'script' and certainly does not fire the code that Rails generates in the onsubmit="..." handler via the form_remote_tag helper.
The solution was to create a new function as the linked answer suggests, but the contents are slightly different:
jQuery.fn.submitWithAjax = function() {
jQuery.ajax({data:jQuery.param(jQuery(this).serializeArray()) + '&authenticity_token=' + encodeURIComponent('<%= form_authenticity_token %>'), dataType:'script', type:'post', url:'/update'});
return false;
};
This is brittle right now-- notice that I insert rails' form_authenticity_token into the Javascript, but really the method (post) and the url (/update) should also be generated rather than hardcoded.
Things are working A-OK now.

Resources