8

I'm on Rails 4.0.

I'm sending an event like this (note the :remote=>true):

<%= button_to 'yes', {controller:'videos', action:'rate', id: video.hashed_id, yesno:'yes'}, {:remote=>true, :class=>"rate-btn yes-btn btn btn-default btn-sm"} %>

My controller looks like this:

  def rate
    video = Video.find_by( hashed_id: params[:id])
    action  = params[:yesno]
    puts video.hashed_id
    puts action

    respond_to do |format|

      if (action=='yes') 
        new_rating = video.rating==1 ? 0 : 1 
        video.update( is_new:0, rating: new_rating )
        format.html { redirect_to controller:'projects', action: show, id: video.project.hashed_id }
        format.json { head :no_content }
        format.js { render :nothing=>true }

      end    

      if (action=='no') 
        new_rating = video.rating==-1 ? 0 : -1
        video.update( is_new:0, rating: new_rating )
        format.html { redirect_to controller:'projects', action: show, id: video.project.hashed_id }
        format.json { head :no_content }
        format.js { render :nothing=>true }
      end

    end

  end

I'm kind of winging it with the format.json/format.html because I don't fully understand which one should apply from an AJAX request.

On the view (where the button lives) I have this:

$(document).ready( function($) {
        console.log('ready');
    $(document).ajaxSuccess( function(data) {alert(data);} )
    $(document).ajaxError( function(data) {alert(data);} )
    $('.rate-btn').closest('form').on('ajax:success', function() {
      console.log('ajax:success!');
    });

    $('.button_to').bind("ajax:success", function() {
    console.log( 'test' );
    });

});

After clicking the button, I get ready in the console, but no matter what I do I can't get the test to show up in the console. What am I missing?

Update:

I tried clicking the button while watching /log/development.log and this is what I see:

Started POST "/videos/rate/lq7lv3218c/yes" for 127.0.0.1 at 2013-08-29 17:14:59 -0400
Processing by VideosController#rate as JS
  Parameters: {"authenticity_token"=>"es4wPqFrxxxxxFsbHQR/gAzofDC+ZwYsiiJ7RAQZUHk=", "id"=>"lq7lv3218c", "yesno"=>"yes"}
  [1m[36mVideo Load (0.3ms)[0m  [1mSELECT `videos`.* FROM `videos` WHERE `videos`.`hashed_id` = 'lq7lv3218c' LIMIT 1[0m
  [1m[35m (0.1ms)[0m  BEGIN
  [1m[36mSQL (0.3ms)[0m  [1mUPDATE `videos` SET `rating` = 0, `updated_at` = '2013-08-29 21:14:59' WHERE `videos`.`id` = 18[0m
  [1m[35m (0.3ms)[0m  COMMIT
  Rendered videos/rate.html.erb (0.0ms)
Completed 200 OK in 7ms (Views: 2.0ms | ActiveRecord: 1.1ms)

I'm a rails n00b but it looks ok to me.

emersonthis
  • 32,822
  • 59
  • 210
  • 375
  • Hm. What is it using? And can I configure it to use jQuery? – emersonthis Aug 29 '13 at 18:59
  • You're binding the ajax:success on the .video-player HTML element which is not the button that "launched" the request, do a bind on the button instead – MrYoshiji Aug 29 '13 at 19:01
  • I tried this and it still doesn't work: ` $('.rate-btn').bind("ajax:success", function() { console.log( 'test' ); }); ` – emersonthis Aug 29 '13 at 19:06
  • @MrYoshiji I also tried binding to the form `.button_to` and still no luck. – emersonthis Aug 29 '13 at 19:12
  • Don't you want `ajaxSuccess`? http://api.jquery.com/Ajax_Events/ – Alex.Bullard Aug 29 '13 at 19:14
  • @MrYoshiji Can you speak to anything that KevinB brought up about a potential mismatch between what library Rails is using and my use of jQuery? – emersonthis Aug 29 '13 at 19:14
  • Also b/c ajaxSuccess is a global event, you need to bind it to `document` – Alex.Bullard Aug 29 '13 at 19:16
  • @Alex.Bullard I'm trying to use Rails native AJAX (which uses custom events) as described here: http://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html – emersonthis Aug 29 '13 at 19:16
  • This post describes a similar issue http://stackoverflow.com/questions/6584708/rails-3-1-ajaxsuccess-handling?rq=1 -- are you sure you're getting json returned properly? – Jeff Paquette Aug 29 '13 at 19:17
  • @JeffPaquette No. I'm not sure at all. Any suggestions how to figure that out? – emersonthis Aug 29 '13 at 19:24
  • Open the Network console (F12 on Chrome or right click > Inspect Element > go "Network"), select the filter "XHR" and see if you get a response after your clicked on your btn remote. (You can also see in your Server console if the server responds to a request when you click on this button) – MrYoshiji Aug 29 '13 at 19:26
  • @MrYoshiji I know for sure that a request is happening because my controller actions work (and make changes in my DB). The thing I don't know that Jeff asked is whether it's returning JSON. – emersonthis Aug 29 '13 at 19:35
  • @MrYoshiji In case I misunderstood something... I do see the request in the console. It's method is `POST` and the type is `text/javascript` and the initiator is `jquery.js?body=1:8707` – emersonthis Aug 29 '13 at 19:45
  • What about `$(document).ajaxSuccess( function(data) {alert(data);} )`? Is it triggered when you fire the ajax button? – MrYoshiji Aug 29 '13 at 20:09
  • @Emerson, did you read the answer in the question I linked? Short answer: bind a handler to ajaxError and inspect the data. It may be a string, not JSON. Or, as the answer suggested try adding content_type: 'text/json' to your render call – Jeff Paquette Aug 29 '13 at 20:12
  • @MrYoshiji I tried and that doesn't trigger either. – emersonthis Aug 29 '13 at 20:16
  • @JeffPaquette I tried adding `$(document).ajaxError( function(data) {alert(data);} )` and I get nothing still. I also tried adding this into the respond_to block: `format.json {render json: 'yes', content_type: 'text/json' } format.js {render json: 'yes', content_type: 'text/json' } `. I still trigger any listeners when I click the button. – emersonthis Aug 29 '13 at 20:22
  • @Emerson, have you looked in the network tab of Firebug to see what your rails app is returning? Have you looked in your rails application log for errors? Basic troubleshooting steps... – Jeff Paquette Aug 29 '13 at 20:27
  • I've been watching the console in Chrome. I see the request in the network tab. But I don't see a response (not sure if I should expect to or not). I was watching the Webrick server for errors, but I'll dig into the Rails logs too and report back. – emersonthis Aug 29 '13 at 20:29
  • @JeffPaquette I updated the question with what I see in /log/development.log. It looks ok to me but maybe I'm missing something. – emersonthis Aug 29 '13 at 21:21

3 Answers3

9

The button_to helper builds a form around a submit button. And the form is what receives the data-remote attribute (see: http://apidock.com/rails/ActionView/Helpers/UrlHelper/button_to). So, I'd try this:

$('.rate-btn').closest('form').on('ajax:success', function() {
  console.log('ajax:success!')
});      

Since you're on Rails 4.0 I'm guessing you're on jQuery 1.9. Binding for events seems to have changed a bit there. So that could be part of it if this has worked for you before. Using .on() for event binding has been preferred over .bind() since 1.7.

Juan Pablo Ugas
  • 1,085
  • 9
  • 22
pdobb
  • 17,688
  • 5
  • 59
  • 74
  • Thanks. I was trying with `.on` originally but when it didn't work. I updated the question with all of the event listeners I'm using for debugging. – emersonthis Aug 29 '13 at 21:12
  • 1
    It would help to see what's returned by the AJAX request too. I usually use the firebug console for this. Are you able to get that? It's highly suspect that the log shows the action rendering an `.html.erb` file for the action (`Rendered videos/rate.html.erb`) when the request clearly came in as a JS request. – pdobb Aug 29 '13 at 21:32
  • I'll fiddle with Firebug. Would the .html.erb issue be caused by the fact that I created a /view/videos/rate.html.erb file? I did it just in case it was necessary (CakePHP requires it) but didn't think it could hurt anything. – emersonthis Aug 30 '13 at 02:10
  • The existence of the `rate.html.erb` file won't influence the controller action. So, for example, if you deleted it I'd expect it to balk about the missing template file then. What happens if you take out the format responders other than the `format.js` one? – pdobb Aug 30 '13 at 02:15
  • Aha! Look at RDX's answer here: http://stackoverflow.com/questions/16637522/controller-can-not-detect-ajax-requests -- basically you don't automatically get to fall int the `format.js` bucket just because you did `remote: true` on a form. You've got to specify the format in the url of the form. – pdobb Aug 30 '13 at 02:31
  • Progress! I added a tag to load jQuery in the layout and it must have been banging into whatever Rails does by default because when I removed my javascript tag, now clicking the ajax button triggers the "ajax:error" event: `$('.rate-btn').closest('form').on('ajax:error', function() { console.log('ajax:error!'); });` So I think the question now is why it's an error. I added `logger.debug request.format` to the controller and it return 'text/javascript'. Is that what it should be? – emersonthis Aug 30 '13 at 12:43
  • Woah MORE news... it turns out my view file couldn't be empty... or something. I seem to have solved the problem but I'm still scratching my head as to what is/was going on. I posted an answer describing what I did and I'd LOVE to hear your thoughts. – emersonthis Aug 30 '13 at 12:59
  • To force the format you would add a `format: json` parameter to the form's post url. Or maybe `format: js`. So in your case: `{controller:'videos', action:'rate', id: video.hashed_id, yesno:'yes', format: 'json'}`. By the way, you should really try to use named routes: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing – pdobb Aug 30 '13 at 14:32
  • Nice explanation indeed. – Arup Rakshit Sep 12 '15 at 20:13
0

Here's what solved the problem for me:

  1. I was loading my own jQuery in the layout (like this: <%= javascript_include_tag "jquery-1.10.2.min", "data-turbolinks-track" => true %> It wasn't throwing any errors, but apparently Rails was also loading its own jQuery and they were somehow conflicting because when I removed mine, all the jQuery still worked and I immediately was able to trigger AJAX error events in the view. But I was still getting ajax errors...

  2. I always had a /views/videos/rate.html.erb file in place just because I wasn't sure if it was necessary or not (in CakePHP it is) BUT I didn't have anything in it. It was blank. I'm not sure why, but when I added a 1 in that view file, all of a sudden I started triggering ajax:success events.

I don't understand why this solved my problem, so if anyone can explain it I'd love to understand better.

emersonthis
  • 32,822
  • 59
  • 210
  • 375
  • It sounds like you've got a gem that's loading jQuery then. (Do you have the `jquery-rails` gem, for example?) Unfortunately, that's how it seems to be going lately with Rails but I'm not sure that's ideal. But that's another topic. I'm not sure why having content in the view file would change the response or in what way that would affect the events... But I still think you should figure out how to ensure you're getting the right format responder based on your request. – pdobb Aug 30 '13 at 14:28
  • I would recommend removing the templates (view files) and format responders that you don't intend to use / have an explicit need for. – pdobb Aug 30 '13 at 14:34
  • 1
    Urgh. I'm still grinding away at this and I'm close, but I still can't get the data to pass from the controller back to the view. In the controller I have `format.json {render json: 'test' }` and in the view I have `$('.rate-btn').closest('form').on('ajax:success', function(e, data, status, xhr) { console.log(data); });`. When I click the button I get `null`. I don't get it. – emersonthis Aug 30 '13 at 15:25
  • It may need to be a valid json object you render. A quick test is to do `format.json { render json: video }`, which will render your `video` object as json (`video.to_json` is implied by the render formatter type). Also, make sure you have the right attribute by using `console.log(e, data, status, xhr)`. Not sure if the `ajax:success` event will pass the data back in the 1st or 2nd attribute. – pdobb Aug 30 '13 at 18:06
  • I tried with `format.json {render json: video }` and I still get `null` for data. I'm logging all four parameters passed by ajax:success so I'm pretty confident I'm checking the right one. – emersonthis Aug 30 '13 at 18:36
  • Aha. I removed `/views/videos/rate.json.erb` and I immediately started throwing errors. So despite my `format.json {render json: video }` Rails is still relying on the view file. Not sure why that is. But when I place a valid json string in that view file I *do* see it in the data parameter. Not sure where to go with this info. – emersonthis Aug 30 '13 at 18:46
  • That must mean that the format wasn't actually `json`. You can put a debugger inside of the block for those formatters and see where it's landing. `format.json { debugger; render json: video }`. If it doesn't hit then it didn't fit. Sounds like it's not hitting any of them maybe? I think you should read through this article. Looks to be great! http://www.alfajango.com/blog/rails-3-remote-links-and-forms/ – pdobb Aug 30 '13 at 19:12
  • And don't forget to read part 2 as well: http://www.alfajango.com/blog/rails-3-remote-links-and-forms-data-type-with-jquery/ -- very informative! – pdobb Aug 30 '13 at 19:14
  • The key here is probably to use the `xhr.responseText` instead of the `data` attribute to get the response data. – pdobb Aug 30 '13 at 19:18
  • Figured it out!! There was a bug higher up in my code that was causing the function to return `nil` BEFORE the `render json: @object` got a chance to work. So that's why the template was rendering and also why the variables weren't getting passed. – emersonthis Aug 31 '13 at 09:54
0

Check if you get a parse error (or similar) by catching ajax:completed. Check the status argument there. You can also set a breakpoint in jquery_ujs.js.

reto
  • 16,189
  • 7
  • 53
  • 67