Delay index rendering until initialization is complete
I am wondering why trying to render the index function is giving me an error cannot call method render of null
. Is there a way to make the index function wait for rendering before the router initialization is complete. The .logs console seems to suggest that the index function try to execute before the ajax call is made on initialization
1.Uncaught TypeError: Cannot call method 'render' of null gallery.js:231
2. XHR finished loading: "http://localhost:3000/readjsonfile". jquery.js:8241
3.success
This is the code. I have a route
var Gallery = Backbone.Router.extend({
routes: {
"": "index",
},
Initially set to null
_index: null,
The Gallery router initialization checks if the index is null and, if so, creates a new view with data.
initialize: function(options) {
var ws = this;
if (this._index === null){
$.ajax({
url: 'galleries',
dataType: 'json',
data: {},
success: function(data) {
console.log("success");
console.log(data);
ws._data = data;
ws._photos = new PhotoCollection(data);
ws._index = new IndexView({model: ws._photos});
console.log(ws._index);
Backbone.history.loadUrl();
},
error: function(r){
console.log("error");
console.log(r);
}
});
return this;
}
return this;
},
This is an index function, placed just after initialization, which displays the view created when initializing above, however I am getting the error Cannot call method 'render' of null
index: function() {
this._index.render();
},
source to share
XHR requests are asynchronous by default, which means that your call $.ajax
will not block and your original route will be immediately reconciled, _index
not yet defined at that point, and you will get an error.
If your code looks like this,
var r = new Gallery();
Backbone.history.start();
what's happening,
-
router.initialize
called - XHR request is issued
-
router.initialize
ends -
Backbone.history.start
starts a pointer route -
router.index
called - XHR request completes
Check out this fiddle to reproduce your issue http://jsfiddle.net/nikoshr/krFtA/
Some things you can do:
-
if possible loading models in your page load which will save your request and some headaches,
-
force the request to sync with
async: false
, which will block until your data is available (basically what you meant, I think):$.ajax({ async: false, url: 'galleries', dataType: 'json', data: {}, success: function(data) { ... }, error: function(r){ ... } })
-
as @fencliff said, delay
Backbone.history.start
until you are ready. -
or with jQuery> = 1.5 you can use deferred object to sync your query and render
var PhotoCollection = Backbone.Collection.extend({ url: 'galleries', initialize: function() { this.loaded = $.Deferred(); }, fetch: function(opts) { var ld = this.loaded, xhr = Backbone.Collection.prototype.fetch.call(this, opts); xhr.always(ld.resolve); } }); var IndexView = Backbone.View.extend({ el: '#v', initialize: function() { _.bindAll(this); }, render: function() { this.$el.html('rendered'); console.log('render'); } }); var Gallery = Backbone.Router.extend({ routes: { "": "index" }, _index: null, initialize: function(options) { this._photos = new PhotoCollection(); this._photos.fetch(); this._index = new IndexView({model: this._photos}); console.log(this._index); }, index: function() { var view = this._index; view.model.loaded.always(view.render); } });
source to share
Backbone.history.start
starts a route that matches the current url, so you need to defer it until the routers are ready.
So instead of calling it loadUrl
in the callback, $.ajax
wait for the dependencies to load and call history.start:
success: function(data) {
//...
Backbone.history.start();
}
source to share