Rails 4 how to catch ajax:success event

16,429

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.

Share:
16,429
emersonthis
Author by

emersonthis

I am a designer, developer, and problem solver. I make websites and stuff. I work with brazen startups, modest individuals, earnest small business, and everyone in between. I care as much about how things look as how they work. I enjoy writing and teaching what I know. The best part about my job is constantly learning new things.

Updated on August 01, 2022

Comments

  • emersonthis
    emersonthis over 1 year

    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
    emersonthis over 10 years
    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.
  • pdobb
    pdobb over 10 years
    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.
  • emersonthis
    emersonthis over 10 years
    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.
  • pdobb
    pdobb over 10 years
    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
    pdobb over 10 years
    Aha! Look at RDX's answer here: stackoverflow.com/questions/16637522/… -- 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.
  • emersonthis
    emersonthis over 10 years
    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
    emersonthis over 10 years
    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.
  • pdobb
    pdobb over 10 years
    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
    pdobb over 10 years
    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: guides.rubyonrails.org/…
  • pdobb
    pdobb over 10 years
    I would recommend removing the templates (view files) and format responders that you don't intend to use / have an explicit need for.
  • emersonthis
    emersonthis over 10 years
    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.
  • pdobb
    pdobb over 10 years
    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.
  • emersonthis
    emersonthis over 10 years
    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
    emersonthis over 10 years
    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.
  • pdobb
    pdobb over 10 years
    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! alfajango.com/blog/rails-3-remote-links-and-forms
  • pdobb
    pdobb over 10 years
    And don't forget to read part 2 as well: alfajango.com/blog/… -- very informative!
  • pdobb
    pdobb over 10 years
    The key here is probably to use the xhr.responseText instead of the data attribute to get the response data.
  • emersonthis
    emersonthis over 10 years
    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.
  • pdobb
    pdobb over 10 years
    Phew! Thanks for the closure :).
  • Arup Rakshit
    Arup Rakshit over 8 years
    Nice explanation indeed.