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();
    },

      

+3


source to share


2 answers


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){
            ...
        }
    })
    
          

    http://jsfiddle.net/nikoshr/m3dW5/

  • 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);
        }
    });
    
          

    http://jsfiddle.net/nikoshr/m3dW5/1/

+1


source


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();
}

      

+3


source







All Articles