Can't get on_complete call-back in in_place_editor to work - scriptaculous

I'm using the in_place_editor capability in a Rails application. When returning from the Ajax call to the server where the database objects have been updated, I would like to use the :on_complete callback to make some additional updates in the view. Unfortunateöly I can't get the :on_complete to work.
To test, this is what I have in the view:
<%= in_place_editor_field "localization", 'sv', {}, {:cols => 11, :on_complete => 'function() {alert(1);}'} %>
Doesn't work and the Javascript console says:
Uncaught TypeError: Object function() {alert(1);} has no method 'bind'
Doing this:
<%= in_place_editor_field "localization", 'sv', {}, {:cols => 11, :on_complete => 'alert(1);'} %>
Results in:
Uncaught TypeError: Object alert(1); has no method 'bind'
Can anybody say what's going on here? Does anybody have an example using on_complete?

If others run into the same problem, I made an ugly workaround which I'm using until somebody comes up with a better answer. I patched the scriptaculous controls.js to check if there is a defined on-completion callback and if there is to call it, as follows:
wrapUp: function(transport) {
this.leaveEditMode();
// Can't use triggerCallback due to backward compatibility: requires
// binding + direct element
// Beginning of ugly patch:
if(typeof editOnComplete == 'function') {
editOnComplete(this.element);
}
// End of ugly patch
this._boundComplete(transport, this.element);
}
});

Related

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.

Rails remote link_to and access to trigger element in javascript response

in my Rails3 application I have table with rows, each containig link_to with :remote => true
...
<tr><td><%= link_to "remote call", action_controller_path(data), :remote => true %>
...
As response to ajax call I return javascript to execute in browser:
# action.js.erb
console.log(this); <-- "this" is browser's window object not my element
$(this).closest("tr")....
The problem is, that in my javascript I need access to element, which triggered the ajax call (the <a> tag). Is there any way how to get access to it?
I found this answer to a similar problem here.
My solution was to bind a pre-submit class to the element, in my case
a popup modal window. It's a similar solution to the post linked to
above in that it uses the pre-submit bindings, but tailored to use
classes instead.
In public/javascripts/application.rb:
jQuery(function($) {
$(".poppable").bind("ajax:loading", function() { $(this).addClass("popped"); });
});
Then in my view for the popup content (e.g.
app/views/mymodel/popup.js.erb):
var p = $(".poppable.popped");
p.removeClass("popped");
/* Do what I need to with p ... */
If this doesn't look kosher, I'm all ears but it works for now.

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

Ruby on Rails3: How do I invoke javascript before an ajax event is fired with remote => true?

<%= link_to( {:controller => 'board',
:action => 'take_turn',
:id => #board.id,
:x => col,
:y => row} , :remote => true, :onClick => "return links_disabled;") do %>
<div class="ttt_square">
</div>
<% end %>
in rails2, there were :before, and :complete params, but I have not found any documentation for this in rails3
As I understand it, this is one of the consequences of Rails 3 using UJS (unobstrusive javascript). Rails 3 enables you to keep the javascript away from e.g. a link-tag. Instead of the link-tag specifying what should be done via javascript, you make the javascript observe the link-tag.
You achieve this by binding a function to a certain event of an object, eg. binding the ajax:before event of the link-tag to a function.
In this blog post the author explains how to do it, in his case with JQuery.
As far as I understand, in Rails 3 you bind the callback events to the element on the client side, and they are fired by rails.js at the appropriate times.
$('#myform').bind('ajax:success', function(){
alert('I succeeded');
})
If I remember well, there is no more support in Rails3.
You could use native jQuery function:
ajaxStart()
http://api.jquery.com/ajaxStart/
See details here: http://www.simonecarletti.com/blog/2010/06/unobtrusive-javascript-in-rails-3/
My version (jquery-rails 0.2.6) supports ajax:before, loading, success, complete, failure, and after. The parameters to the success/failure functions are not the same which has tripped me up in the past. But the following works for me:
$('a').bind('ajax:loading', function() {
alert('here');
});
If your link element was created after the initial page load, you might need to bind using 'live':
$('a').live('ajax:loading', function() { alert('...'); });
I would also double-check that your onclick handler is not interfering.

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