1


I am new to RequireJS and Backbone and was trying to understand why the ajax (fetch) code is not working as excepted.

main.js

require.config({
shim: {
    'backbone': {
        deps:['underscore', 'jquery'],
        exports: 'Backbone'
    },
    'underscore': {
        exports: '_'
    }
},
paths: {
    'jquery': 'vendor/jquery/jquery',
    'underscore': 'vendor/underscore/underscore',
    'backbone': 'vendor/backbone/backbone'
}
});
require(['views/appViews'], function(AppView) {
 new AppView();
});

AppView.js

define(['jquery', 'underscore','backbone', '../collections/appCollections'], function($, _, Backbone, AppCollections) {
    var App = Backbone.View.extend({
        initialize: function() {
            _.bindAll( this, "render" );

            this.collection = new AppCollections;

            var $this = this;

            this.collection.bind("all", this.render, this);
            var x = this.collection.fetch();
            /*
             * This was not working
            this.collection.fetch({
                success: function() {
                    $this.render();
                }
            });
            */
        },

        template: _.template( $('#tweetsTemplate').html() ),

        render: function() {
            console.log(this.collection.toJSON());
            //$(this.el).html(this.template({ tweets: this.collection.toJSON() }));
        }   
      });
      return App;
     });

AppCollections.js

define(['jquery','underscore','backbone','../models/appModels'], function($, _, Backbone, AppModel) {
 var AppCollection = Backbone.Collection.extend({

model: AppModel,

url: 'http://search.twitter.com/search.json?q=dog',

parse: function ( response, xhr ) {
        return response.results;
},
// Overwrite the sync method to pass over the Same Origin Policy
sync: function (method, model) {
    var $this = this;
    var params = _.extend({
        type: 'GET',
        dataType: 'jsonp',
        url: $this.url,
        processData: false
    }   );

    return $.ajax(params);
}

 });
 return  AppCollection;
});

AppModel

define(['underscore', 'backbone'], function(_, Backbone) {
var AppModel = Backbone.Model.extend({});
return AppModel;
});

Problem is: the render method is not called once collection is fetched. Also no error in developer tool. So not sure where to look.

enter image description here

Any pointer is helpful.

Thanks Viral

Viral
  • 310
  • 3
  • 19

3 Answers3

1

When you overwrite the sync method in backbone it will not trigger the events properly. Try overwriting the sync method this way

Or, you can simply make your success function look like backbones source:

success = function(resp) {
    if (success) success(model, resp, options);
    model.trigger('sync', model, resp, options);
};
Community
  • 1
  • 1
Ryan George
  • 186
  • 9
  • thanks ryan..your answer was correct as well..but was not sure..where to put that code..and why...Paul provided...that explanation so accepted his answer – Viral Mar 01 '13 at 21:14
  • His answer was much better than mine! I didn't deserve to win. – Ryan George Mar 01 '13 at 21:26
1

The success callback is not called because your sync method is not passing it on to ajax.

The third parameter of sync is the options object, which has the success callback in it.

sync: function (method, model, options) {
    var $this = this;

    var success = options.success;
    options.success = function(resp) {
      if (success) success(model, resp, options);
      model.trigger('sync', model, resp, options);
    };
    var params = _.extend({
        type: 'GET',
        dataType: 'jsonp',
        url: $this.url,
        processData: false
    }, options);

    return $.ajax(params);
}

This way, ajax will properly call the success callback defined in Backbone Collection's fetch which will in turn call the success callback you passed into fetch.

Then fetch:

        this.collection.fetch({
            success: function() {
                $this.render();
            }
        });

Here is fetch from Backbone source. You can see it passes the success callback to sync.

fetch: function(options) {
  options = options ? _.clone(options) : {};
  if (options.parse === void 0) options.parse = true;
  var success = options.success;
  options.success = function(collection, resp, options) {
    var method = options.update ? 'update' : 'reset';
    collection[method](resp, options);
    if (success) success(collection, resp, options);
  };
  return this.sync('read', this, options);
},
Paul Hoenecke
  • 5,060
  • 1
  • 20
  • 20
  • yes when I tried to add options as third parameter it give me following error: --> Uncaught TypeError: Object # has no method 'reset' backbone.js:816 _.extend.fetch.options.success backbone.js:816 jQuery.Callbacks.fire jquery.js:1037 jQuery.Callbacks.self.fireWith jquery.js:1148 done jquery.js:8074 jQuery.ajaxTransport.send.script.onload.script.onreadystatechange – Viral Mar 01 '13 at 20:56
  • Yeah..when debugging the code..I found out the options pass to fetch function is undefined..not sure do I need to explicit pass options.success? – Viral Mar 01 '13 at 20:59
  • OK, you get that error because `ajax` expects a success callback with different parameters, so you can see in Backbone `sync` what it is doing, added it to the code above. – Paul Hoenecke Mar 01 '13 at 21:02
  • hmm...thank you so much that solved my problem...I'll read more on sync & fetch documentation...to understand the flow.. – Viral Mar 01 '13 at 21:13
  • Yes it can be hard to follow. `ajax` calls the `success` callback defined in `sync`, then that callback calls the one defined in `fetch`, which eventually calls the callback you passed into fetch. Normally you don't need to worry about that, if you don't override fetch or sync. – Paul Hoenecke Mar 01 '13 at 21:15
0

Great response Paul, but just wanted to point out the following:

When attempting to retrieve the data from your ajax call by overriding fetch's success function, I had to make the following modification to your code:

sync: function (method, model, options) {
    var $this = this;

    var success = options.success;
    options.success = function(resp) {
      if (success) success(resp);
      model.trigger('sync', model, resp, options);
    };
    var params = _.extend({
        type: 'GET',
        dataType: 'jsonp',
        url: $this.url,
        processData: false
    }, options);

    return $.ajax(params);
}

Note the difference in the line:

if (success) success(resp);

This was needed in order to properly pass the success function the response, otherwise it was being overwritten by the model. Now, in the success function of fetch, you can output the data:

var $this = this;
this.collection.fetch({
    success: function(collection, response, options){
        $this.render(response);
    }
});

This passes on the ajax data (response) to the render function to do what you like with. Of course, you could also manipulate the data in any which way beforehand as well.

Ideally, I'd like to be able to pass the data into the collection.models object, as Backbone does by default. I believe it has something to do with how the data is being parsed, but I haven't figured it out yet. If anyone has a solution, I'd love to hear it :)

Update:

I've managed to override the parse function and process the JSON data from my ajax call in such a way so as to stay true to the way that Backbone structures its collection object. Here's the code:

parse: function(resp){
    var _resp = {};
    _resp.results = [];
    _.each(resp, function(model) {
        _resp.results.push(model);
    });
    return _resp.results;
}

This creates a new object with an array of your models called results, which is then returned to your fetch function, allowing you to directly access the attributes of each model.