3
votes

I'm having problems resorting my collection upon a click event. The sorting function is triggered and executes for each model but neither the reset event is gets triggered nor the collection changes on the view.

I have multiple sort criterias defined on my collection like:

feNoRequire.Collections.CompetitionCollection = Backbone.Collection.extend({

    model: feNoRequire.Models.CompetitionModel,
    comparator: function (property) {
        return selectedStrategy.apply(model.get(property));
    },
    strategies: {
        name: function (competition) { return competition.get("name"); }, 
        nameReverse: function (competition) { console.log(competition); return -competition.get("name"); }, 
        date: function (competition) { console.log(competition.get("event")); },
    },
    changeSort: function (sortProperty) {
        this.comparator = this.strategies[sortProperty];
    },
    initialize: function () {
        this.changeSort("name");   
    }

});

And on my view file:

initialize: function(options){
        this.evnt = options.evnt;

        this.collection.on('reset', this.render, this);     
        this.evnt.bind("orderByDate", this.changeSort, this);
    },

    changeSort: function(){
        this.collection.changeSort('nameReverse')
        this.collection.sort();
    },

    render: function() {
        console.log("going for rendering")
        var renderedContent = this.template({competitions: this.collection.toJSON()});

        $(this.el).html(renderedContent);
        return this;
    }

Any idea on how to solve this?

EDIT After the answers bellow the render is now triggered but the objected only get sorted on initialize. Any subsequent sorts return the collection in the initial order - this.changeSort("name");

my model:

feNoRequire.Models.CompetitionModel = Backbone.Model.extend({
    initialize: function(){
        this.attributes.events = new feNoRequire.Collections.EventCollection(this.attributes.events);
    }
});
3
'this.attributes.events = new feNoRequire.Collections.EventCollection(this.attributes.events);' <- out of curiousity, what's the purpose of this?1nfiniti
For each model, I have a list of events which I want to be models as well so I can sort them.jribeiro
a late comment, for sure, but semantically, I would move the manual call to this.collection.sort() in the View.changeSort into the Collection's function, via this.sort(); this way you know when you call changeSort() on the collection, it will always re-sort itself.mix3d

3 Answers

6
votes

From the fine manual:

sort collection.sort([options])

[...] Calling sort triggers a "sort" event on the collection.

So calling sort doesn't trigger a "reset" event (because the collection doesn't get reset), it triggers a "sort" event. So you want to:

this.collection.on('sort', this.render, this);

as well as binding to "reset".

Demo: http://jsfiddle.net/ambiguous/34Ena/


I see that you're calling changeSort('nameReverse') and that that sorting does this:

nameReverse: function (competition) {
    return -competition.get("name");
}

That won't do what you think it does, negating a non-numeric string will give you NaN. That means that you'll end up trying to sort a list of NaNs and all of these are false:

NaN  < NaN
NaN  > NaN
NaN == NaN

so sorting a list of NaNs does nothing useful. You'll have to use a two argument comparator function if you want to reverse sort strings:

nameReverse: function(a, b) {
    a = a.get('name');
    b = b.get('name');
    return a < b ?  1
         : a > b ? -1
         :          0;
}
1
votes

You may listen to collection's sort event and perform render. Try changing event binding to:

this.collection.on('sort reset', this.render, this);     
1
votes

Very nice code setup. I really like the way you're calling your sort functions via the strategies object.

I think the issue with this code is stemming from the following section:

comparator: function (property) {
    return selectedStrategy.apply(model.get(property));
},

as per the documentation, the comparator function takes a model or iterator (not a property), also not sure what selectedStrategy is supposed to be referencing... is this another function that exists somewhere outside the code you've provided?

Apply also takes a context object to act as "this", and an array of arguments. As per the MDN documentation on apply:

fun.apply(thisArg[, argsArray])

unless your model's property is an object that you want to use as "this", i don't see this working properly. Maybe it would make more sense if I could see the model definition as well.

EDIT: After reading the other responses that came in, I realized you're watching the reset event instead of the sort, so my response is probably just a misunderstanding of your code, and not the problem afterall :)