Dennis
Dennis

Reputation: 939

Backbone collection fetch imported incorrectly

I have a collection which is fetched from a REST endpoint, where it receives a JSON.

So to be completely clear:

var Products = Backbone.Collection.extend({
    model: Product,
    url : 'restendpoint',

    customFilter: function(f){
        var results = this.where(f);
        return new TestCollection(results);
    }

});

var products = new Products();

products.fetch();

If I log this, then I have the data. However, the length of the object (initial) is 0, but it has 6 models. I think this difference has something to do with what is wrong, without me knowing what is actually wrong.

Now, if I try to filter this:

products.customFilter({title: "MyTitle"});

That returns 0, even though I know there is one of that specific title.

Now the funky part. If I take the ENTIRE JSON and copy it, as in literally copy/paste it into the code like this:

var TestCollection = Backbone.Collection.extend({

    customFilter: function(f){
        var results = this.where(f);
        return new TestCollection(results);
    }
}); 

var testCollectionInstance = new TestCollection(COPY PASTED HUGE JSON DATA);
testCollectionInstance.customFilter({title: "MyTitle"});

Now that returns the 1 model which I was expecting. The difference when I log the two collections can be seen below. Is there some funky behaviour in the .fetch() I am unaware of?

Edit 2: It may also be of value that using the .fetch() I have no problems actually using the models in a view. It's only the filtering part which is funky.

Edit 3: Added the view. It may very well be that I just don't get the flow yet. Basically I had it all working when I only had to fetch() the data and send it to the view, however, the fetch was hardcoded into the render function, so this.fetch({success: send to template}); This may be wrong.

What I want to do is be able to filter the collection and send ANY collection to the render method and then render the template with that collection.

var ProductList = Backbone.View.extend({
    el: '#page',

    render: function(){
        var that = this; /* save the reference to this for use in anonymous functions */
        var template = _.template($('#product-list-template').html());
        that.$el.html(template({ products: products.models }));
        //before the fetch() call was here and then I rendered the template, however, I needed to get it out so I can update my collection and re-render with a new one (so it's not hard-coded to fetch so to speak)
    },

    events: {
        'keyup #search' : 'search'
    },

    search : function (ev){
        var letters = $("#search").val();

    }

});

Difference when logged

Edit: New image added to clearify the problem Model filtering differences

Upvotes: 1

Views: 263

Answers (2)

Maroshii
Maroshii

Reputation: 4017

It's a bit tricky, you need to understand how the console works.

Logging objects or arrays is not like logging primitive values like strings or numbers. When you log an object to the console, you are logging the reference to that object in memory. In the first log that object has no models but once the models are fetched the object gets updated (not what you have previously logged!) and now that same object has 6 models. It's the same object but the console prints the current value/properties.

To answer your question, IO is asynchronous. You need to wait for that objects to be fetched from the server. That's what events are for. fetch triggers a sync event. Model emits the sync when the fetch is completed.

So:

var Products = Backbone.Collection.extend({
    model: Product,
    url : 'restendpoint',

    customFilter: function(f){
        var results = this.where(f);
        return new TestCollection(results);
    }
});

var products = new Products();

products.fetch();

console.log(products.length); // 0

products.on('sync',function(){
  console.log(products.length); // 6 or whatever
  products.customFilter({title: 'MyTitle'});
}) 

Upvotes: 2

Garrett Murphey
Garrett Murphey

Reputation: 135

It seems like a response to your ajax request hasn't been received yet by the time you run customFilter. You should be able to use the following to ensure that the request has finished.

var that = this;

this.fetch({
    success: function () {
        newCollection = that.customFilter({ title: 'foo' });
    }
});

Upvotes: 0

Related Questions