Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone collection comparator

I'm using backbone and marionette,

and I want to sort my collection and rendering view.

but something weird happening.

'/api/note/getList', it returns (and it called when collection be initialized by view)

[{"id":22,"name":"Test2","isPublic":1},{"id":11,"name":"Test1","isPublic":1},{"id":33,"name":"Test3","isPublic":1}]

and this is my collection,

define [
    'models/Note'
],
(Note) ->
    class NoteCollection extends Backbone.Collection

        model : Note
        url : '/api/note/getList'

        comparator : (item) =>
            console.log item.get 'id'
            return item.get 'id'

and the console.log print

22
22
11

print '22' twice? also it does not sorting neither.

How should I do to make sorting the collection?

[EDIT]

This is my compisteView that initialize collection

define [    
    'hbs!./noteCollection_tpl'
    './noteItemView'
    'collections/NoteCollection'
],
(noteCollection_tpl, noteItemView, NoteCollection) ->
    class NoteCollectionView extends Backbone.Marionette.CompositeView
        template : noteCollection_tpl
        itemView : noteItemView
        itemViewContainer : '.noteListContainer'
        className : 'noteWrap'

        initialize : (options) ->
            @collection = new NoteCollection() 

@collection = new NoteCollection() => this run fetch automatically i think.

like image 250
Expert wanna be Avatar asked Dec 25 '22 17:12

Expert wanna be


1 Answers

The problem is that you're using a bound function as the comparator:

comparator : (item) =>

and that is confusing Backbone's "how many arguments does the comparator take" check. From the fine manual:

comparator collection.comparator

[...] A comparator can be defined as a sortBy (pass a function that takes a single argument), as a sort (pass a comparator function that expects two arguments), [...]

And if we look inside sort, we see this:

if (_.isString(this.comparator) || this.comparator.length === 1) {
  this.models = this.sortBy(this.comparator, this);
} else {
  this.models.sort(_.bind(this.comparator, this));
}

So if the comparator is a function that takes one argument (i.e. comparator.length === 1), then sortBy will be used and the comparator will get one argument; however, if the comparator is a function that doesn't take one argument then the standard sort is used and the comparator will be passed two arguments.

If you watch your comparator being called, you'll see that it gets two arguments. How could that happen? That would happen if comparator.length was not one. Your comparator ends up with a length of zero due to the way CoffeeScript implements =>; for example:

class C
    m1: (x) -> x
    m2: (x) => x

c = new C
console.log(c.m1.length)
console.log(c.m2.length)

will give you 1 and 0 in the console.

Demo: http://jsfiddle.net/ambiguous/GAAap/

If you look at the JavaScript for this:

class C
    m: (x) => x

You'll see that this function is used

__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

to generate => methods. Notice the return function() { ... } in there? That means that every => method will claim to have a length of zero. Congratulations, I think you have rediscovered a bug in CoffeeScript.

If you use a standard -> method for your comparator:

comparator: (item) -> item.id

then CoffeeScript won't screw up your comparator's length value and your sorting will start to make sense.

Demo: http://jsfiddle.net/ambiguous/WXDcJ/


Looks like epidemian has already reported this bug:

https://github.com/jashkenas/coffee-script/pull/2872


Execute Summary: Don't use => for Backbone collection comparator functions.

like image 166
mu is too short Avatar answered Dec 28 '22 07:12

mu is too short