开发者

backbone collection keeping sort order on model mutation

If I have a collection with a comparator. ( in coffeescript )

class Words extends Backbone.collection
    comparator: (word)->
        word.get('score')

how do I keep the collection sorted if I am changing the score of the underlying items. The idea is to attach this to a list view where items with the lowest score are always at the top.

I've been manu开发者_高级运维ally calling sort on the collection every time I mutate an instance but this doesn't seem too efficient given that the whole list is sorted with one item.

I might perhaps try removing the mutated item and then add it again.

Any suggestions?


It looks like the rendering code is highly inefficient for one simple reason: DOM manipulation is expensive. Whenever possible, you should manipulate the DOM once rather than several times. All other optimization in JavaScript/CoffeeScript is secondary.

Here's the salient code (from the gist linked to from the second comment on ambertch's response):

refresh: ->
    @list.listview("refresh")

appendWord: (word)->
    wv = new WordView({model: word}).render().el
    @list.append( wv )
    @refresh()

render: ->
    @list.html("")
    @model.each (word) => @appendWord(word)
    @refresh()

So, on render, first the list's HTML is cleared; then for each word the list's HTML is cleared and the control is refreshed!

I'm not familiar with jQuery Mobile, so I'm not sure whether the main penalty is incurred on append (as it is in jQuery) or on listview('refresh'), but it's easy to rewrite the loop to avoid both, and remove some function defining/calling overhead as well:

render: ->
  html = []
  @model.each (word) ->
    html.push(new WordView(model: word).render().el)
  @list.empty().append(html)

Now you've got just one html setter and one refresh call, rather than one html setter, n append calls, and n+1 refresh calls!


How about binding all of a Words' initial models to sort on a change event, and then bind the same on an add event to the collection?

_(self).models.each(model) { model.bind('change', self.sort) } // bind on the model add as well

Backbone.Collection.sort triggers a refresh, so then just:

this.bind('refresh', this.refresh)

As to your concern about efficiency, what do you mean by "the whole list is sorted with one item?" Unless you have THOUSANDS of items (and even then, run some benchmarks in various browsers, I bet it would still be pretty damn fast), it shouldn't make much difference with modern browsers/computers

If you were really concerned about it, I guess you can do some of the optimization techniques for keeping larger sets sorted. For example, drop them in score buckets (0-100, 100-500, 500-1000, etc.), then drop the score in the bucket and only sort within the bucket (the actual sort algorithm is per browser implementation, Backbone - or Underscore rather, just calls it). You can google for algorithms like this. But it's a lot of work for little gain vs. the increase in code complexity.

Again, though, you'd need a lot of profiling and benchmarking to determine if this is actually worth it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜