AlexNikolaev94 AlexNikolaev94 - 1 month ago 11
Javascript Question

Rendering the view returns undefined

I've got a collection view with two filter methods, and a render method which takes a parameter. The problem I'm stuck with is that when rendering the view for the first time it returns me an error. Here's my collection:

var ResumeCollection = Backbone.Collection.extend({
url: 'http://localhost:3000',
filterActive: function () {
var active = this.where({interviewed: false});
return new ResumeCollection(active);
},
filterInterviewed: function () {
var interviewed = this.where({interviewed: true});
return new ResumeCollection(interviewed);
}
});


And my view:

var ResumeList = Backbone.View.extend({
events { // hash array of filter events },
initialize: function () {
this.collection.fetch();
},

render: function (filtered) {
var self = this;
var data;

if (!filtered) {
data = this.collection.toArray();
} else {
data = filtered.toArray();
}

_.each(data, function (cv) {
self.$el.append((new ResumeView({model: cv})).render().$el);
});

return this;
},

showActive: function (ev) {
var filtered = this.collection.filterActive();
this.render(filtered);
},

showInterviewed: function (ev) {
var filtered = this.collection.filterInterviewed();
this.render(filtered);
},

showAll: function (ev) {
this.render(this.collection);
}
});


This view gets rendered for the first time in my router by passing a collection:

var AppRouter = Backbone.Router.extend({
routes: {
'': 'home'
},

initialize: function () {
this.layout = new LayoutView();
}

home: function () {
this.layout.render(new ResumeList({
collection: new ResumeCollection()
}));
}
});


And this is the layout view within which all the other views are rendered:

var LayoutView = Backbone.View.extend({
el: $('#outlet'),
render: function (view) {
if (this.child && this.child !== view) {
this.child.undelegateEvents();
}

this.child = view;
this.child.setElement(this.$el).render();
return this;
}
});


When I just refresh my page, I get
filtered.toArray is not a function
error and nothing is rendered respectively. After inspecting everything in the debugger, I found out that when the view gets rendered for the first time, the
filtered
attribute receives an empty collection, assigns it to
data
variable, which becomes an empty array and goes to the body of
render
function, becoming
undefined
after that. The mysteries go here: whenever I click items, that are bound to my
show*
events, they act exactly as expected and render either models where
interviewed === false
, or
true
or the whole collection. This looks kinda magic to me and I haven't got the faintest idea what can I do with that.

ADDED: GitHub repo with this project

Answer

After a piece of advice from my mentor I realized that the core of the problem was in asynchronous origin of fetch method -- as I passed this.collection.fetch in my initialize function, it executed after my render method, not before it, so my render method had just nothing to render when the view was called for the first time. So, this fix worked:

var ResumeList = Backbone.View.extend({
    initialize: function (options) {
         this.collection = options.collection();
         // removed .fetch() method from here
    },
    render: function (filtered) {
        var self = this;
        var data;
        // and added it here:
        this.collection.fetch({
            success: function (collection) {
                if (!filtered) {
                    data = collection.toArray();
                } else {
                    data = filtered.toArray();
                }
                self.$el.html(self.template(collection.toJSON()));
                _.each(data, function (cv) {
                   self.$el.append((new ResumeView({model: cv})).render().$el);
               })
            }
        });
    }
});

And this worked perfectly and exactly as I needed.

Comments