best_in_place - use updated value back in view - ruby-on-rails

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.

Related

Is Remote Available for DIV/SPAN in Rails 4?

For example, to use for the following:
#index.haml
%article-content{ :remote => true, :id => "article-#{article.id}", :class => "article-content", "data-article-id" => article.id }
#show.js.erb
var article = "#article-<%= params[:article_id] %>";
$('article').html('<%= j render ("article") %>').show();
So far, this does not appear to work.
EDIT:
Thanks to #MrYoshiji, I seem to get closer to achieving this. However, I can still see some raw JS in the browser and images do not load from the view. And, the articles do appear even though there is not element in the jquery selector below, so I'm a bit confused, and trying to figure it out.
#articles/show.js.erb
$().html('<%= j render ("article") %>');
#articles_controller.rb
def show
respond_to do |format|
format.js { }
end
end
There is very nice solution I saw someday but I can't find it back. I will try to show you the basics of this solution:
# articles/index.haml
.ajax_load.article-content{ data: { 'remote-url' => article_path(article) } }
= 'This content will be available soon'
# layouts/application.haml
:javascript
$(document).ready(function() {
$('.ajax_load').each(function(index, element) {
var url_to_go = $(element).data('remote-url')
if (url_to_go) {
$.get(url_to_go, function(responseText) {
$(element).html(responseText);
})
} else {
console.log('missing url for ajax-load!')
}
})
})
This javascript will detect every HTML element having the class .ajax_load, loop through each and make an AJAX call to its 'data-remote-url' attribute (if exists) and load the result of the AJAX call into the HTML element itself. With this solution, you can use the combination HTML class ajax_load + data-remote-url to load in AJAX anything in your page.
No, it is not. :remote is used with helpers such as form_for, link_to, and button_to. Check out the Guide. You cannot stick :remote any old place and expect it to do something.

format.js is not manipulating dom once action caching enabled

Note: I am presenting a logic here what I am doing.
What I am doing:
Think about the basic index action where we are listing products and with pagination. Now using remote-true option I have enabled ajax based pagination. So far things works perfectly fine. Take a look on sample code.
Products Controller:
def index
#products = Product.paginate(:order =>"name ASC" ,:page => params[:page], :per_page => 14)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #products }
format.js
end
end
Index.html.erb
<h1>Products</h1>
<div id="products">
<%= render "products/products" %> // products partial is just basic html rendering
</div>
<script>
$(function(){
$('.pagination a').attr('data-remote', 'true')
});
</script>
Index.js.erb
jQuery('#products').html("<%= escape_javascript (render :partial => 'products/products' ) %>");
$('.pagination a').attr('data-remote', 'true');
So whats the problem:
Now I want to enable action caching on this. But index.js.erb file is not manipulating DOM. If I remove the remote-true functionality then things works fine with caching.
For action caching I have added this line on the top of the controller:
caches_action :index, :cache_path => Proc.new { |c| c.params }
Any suggestions?
Update:
Problem is jquery code is not executing. From this question
I found out what's wrong. jQuery actually surround the incoming script with a so that the browser evaluates the incoming code. But the caching mechansim merely saves the code as text and when one re-request, it returns the code as text but not evaluate it. Therefore, one needs to eval the code explicitly
But how to solve this problem??
After some trial and error, I think I have a work around.
Page links
Instead of
$(function() { $('.pagination a').attr('data-remote', 'true') });
use
$(function() {
$('.pagination a').click(function() {
$.ajax({
url: this.href,
dataType: 'script'
});
return false;
});
});
so response created by the app server will be run as javascript
Controller
next, change your caches_action line to
caches_action :index, cache_path: proc { |c| c.params.except(:_).merge(format: request.format) }
since ajax appends a _ params for some sort of timestamp
Hopefully this works :)
I don't see what the issue should be with using remote: true. Someone else suggested to use .ajax instead of remote: true, but that's exactly what the remote functionality does, so there shouldn't be any difference.
The other answer has code that explicitly uses jQuery.ajax, but the only difference in their code compared to what the remote functionality does is that they're specifying an explicit dataType. You can actually do that with remote: true though.
In your HTML link, you just need to specify data-type="script". Or, based on your posted JS, you'd do this:
$(function(){
$('.pagination a').attr('data-remote', 'true').attr('data-type', 'script');
});
EDIT: Also, I wrote more in-depth about the data-type attribute and how it works with Rails here: http://www.alfajango.com/blog/rails-3-remote-links-and-forms-data-type-with-jquery/
I think I found a solution to this problem. I have a controller that has caches_action for an action that uses format.js to fetch some data via ajax, and it was not working out of the box.
I found that, despite the request being transmitted to the server, and the server correctly parsing the request and "rendering" the index.js.erb template, nothing was updating in the DOM. Your solution with $.ajax and dataType:'script' fixed the problem for me, however, I didn't like having to do jquery to bind to a click on a link, which should happen by default... I was able to make it work correctly by changing my link_to to this:
= link_to "click me", user_action_path(params), remote: true, data:{type: 'script'}
Hope this helps!
I've been having the same problem but on my 3.0.3 application with :remote => true I have added :'data-type' =>: script and have been worked fine.
However, in my case, I don't see improvement when loaded by ajax the list.

Pass js variable to server code

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.

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

Javascript on page is not executing before AJAX onComplete event is called

I have a form that makes an Ajax POST request to insert a widget into my database. In the form, I have a select box where you can select from the widgets. After the db insert is made, I must update the select box. I actually just replace the entire form for now.
Because the select box has the widgets, I must have a copy of the objects in javascript. I call this var widget_objects. When the form is replaced during the update event, I print the ruby variable <%= #widget_objects %> and I can see the newly created object. However, when I try to access the javascript var "widget_objects" in the onComplete event, the new object does not exist. I create the javascript widget_objects with this line of code on the page:
widget_objects = <%= #widget_objects %>;
So it seems that the line of code above is not executed before Ajax request's onComplete event. However, I thought the onComplete event occurs after the page has been loaded, and I would assume after scripts are eval'd....any ideas?
<%= submit_to_remote(
"save_widget",
"Save Widget & Generate Embed Code",
{
:url => widgets_url(:user_id => #user.id),
:update => "widget_form",
:method => :POST,
:html => { :id => "save_widget_button",
:onclick => "this.value='Saving...'; this.disabled = 'true';",
:style => "width: 220px;"
},
:complete =>"
$('save_widget_button').disabled='';
$('save_widget_button').value='Save Widget & Generate Embed Code';
var last_id = $j('select#widget_id').children(':last').attr('value');
alert( widget_objects[last_id] );
",
:success => "reportMessage('success', request.headerJSON.success, 'save_widget_status'); $('band_form').reset();",
:failure => "reportMessage('failure', request.headerJSON.errors, 'save_widget_status');"
}) %>
When dealing with these kinds of problems, use Firefox' JS debugger (install the Firebug add-on), enable automatic breaking on exception (from the Scripts tab), and reload the page. If Firefox intercepts an exception (e.g. originating from within the response rjs) then fix the exception. You may also want to just surround your rjs and :complete code with a 'try { ... } catch(e) { alert(e) }' JS wrapper.
Next, use the JS debugger to set breakpoints inside prototype.js's respondToReadyState method, where you'll be able to inspect the rjs reply from your app and step through the evalResponse() method. You'll narrow it down pretty quickly.
I had something similar to this happening in my PHP scripts, where the timing of the scripts seemed out of sync. It was more about how code that appeared AFTER the ajax call would execute before the code in the onSuccess event, and the solution was to wrap the entire event response code inside an anonymous function, like this:
onSuccess: function(reply)
{
resp = reply.reponseText();
pieces = resp.split('|');
(...etc...)
}
You might try something similar after your :complete identifier. Hope this helps!
Setting EvalScripts to true must work.
else you can try loading the script using
> <script> function window.onload(){
> alert('I am loaded as the page
> completes loading') } </script>
if this does not help then you can call function :loading in ajax calls while the response is received the function will be called.

Resources