首先我想說的是這篇文章的題目起的很怪,由於我不知道起個什麼名字比較好。渲染列表是咱們應用中最多見的操做了吧,在運用Backbone的應用中,咱們通常會把列表做爲一個Collcetion,而後指定一個View去顯示這個Collection,很方便。但當你須要對一個集合進行過濾操做,只顯示Collection中符合條件的那部分呢?html
渲染一個內存中存在的Collection毫無疑問很簡單,由於Backbone提供了一些操做的方法。可是當對一個Collection進行過濾操做以後,渲染過濾以後的結果你就會遇到一些意想不到的困難。本文將會尋找簡單的代碼來解決這個問題。api
基本的過濾操做數組
Backbone.Collection有一些方法來實現過濾和搜索。其中大多數都是繼承自Underscore.js,但有些不是。然而無論它們從哪裏來,大部分的這些方法都有一個共同點:返回Collection中的Models數組。this
例如where
方法,當調用這個方法的時候會返回具備你指定的屬性值的Model數組。spa
var testCollection = new Backbone.Collection([
{name: "joy", age: 21}, {name: "jack", age: 20}, {name: "pater", age: 32}, {name: "daivd", age: 22}, {name: "gina", age: 21} ]); var results = testCollection.where({ age : 21 });
results
將會是一個數組,裏面有全部{age: 21}
的model
,咱們在console
裏面看一下:code
經過視圖控制過濾regexp
顯示過濾列表是至關容易的。惟一的問題是過濾操做以後返回的並非一個Collection,而是一個Model數組。這每每會大吃一驚,捉雞啊。怎麼處理這個數組呢?如何讓一個Backbone.View顯示它呢?此外,當你須要顯示這個過濾操做以後的數組的View已經指向了一個原始的Backbone.Collection的時候會讓這個問題變得更加棘手。htm
你可能會說,這很容易啊,來看看下面的操做:對象
var FilteredView = Backbone.View.extend({ events: { "click #run": "runFilter" }, runFilter: function(e){ e.preventDefault(); this.filter = { // ... get the filter info from the DOM }; this.render(); }, render: function(){ var html = [], template = _.template($("tmpl-demo").html()); var filteredList = this.collection.where(this.filter); _.each(filteredList, function(item){ html.push(template(item.toJSON()); }); this.$el.html(html); return this; } });
在必定程度上看起來都不錯。代碼很短,很容易理解,也解決了問題。blog
可是上面的代碼只符合一些簡單的場景,若是你正在處理簡單的狀況,可能這樣沒有作錯什麼。但有存在潛伏的缺陷,當你繼續須要使用過濾操做的時候,你會發現上面的代碼以後沒發進行進一步的過濾操做了。
怎麼解決這個問題呢?讓咱們回到原點,咱們能夠對Backbone.Collection進行各類過濾操做,對,你是否是想到了什麼呢?因此咱們要讓過濾操做以後返回的不是一個數組,而是一個Backbone.Collection對象。可是怎麼實現呢?
自過濾反模式
若是你試圖在原有Collection的基礎上進行過濾以後再更新這個Collection,你最終會步入糟糕的境地,甚至比在View裏面進行過濾操做更糟糕。
不過,仍是來看看如何實現上面這種方式吧:
var testCollection = Backbone.Collection.extend({ customFilter: function(filters){ var results = this.where(filters); this.reset(results); } }); var testCollection = new Backbone.Collection([ {name: "joy", age: 21}, {name: "jack", age: 20}, {name: "pater", age: 32}, {name: "daivd", age: 22}, {name: "gina", age: 21} ]); // filter the collection testCollection.customFiler({ age : 21 });
一旦你經過自定義方法過濾Collection,Collection中便只有兩個符合條件的項。這彷佛是理想的結果,若是你想使用一個新的過濾器,它將只會在上次的結果中進行進一步的過濾,由於以前的Collection已經被更新了,這在大多數時候並非咱們想要的結果。
建立過濾後的Collection
若是你想用Backbone.Collection處理過濾的數組,你必須將數組轉變成Backbone.Collection。這會出現兩個Collection:原始的Collection和過濾以後的Collection,當你有了有了過濾後的Collection後,你就可使用 Backbone.View來直接渲染了。
var results = testCollection.where({ age: 21 }); var filteredCollection = new Backbone.Collection(results);
是的,就是這麼簡單。很歡樂吧,但仍是能夠變得更加方便。
返回同種Collection
你可能想從自定義Backbone.Collection返回從一個過濾後的同類型的一個實例。
var testCollection = Backbone.Collection.extend({ customFilter: function(filters){ var results = this.where(filters); return new testCollection(results); } }); var filteredCollection = testCollection.customFilter({ age: 21 });
更新過濾操做
當過濾條件改變的時候,你須要更新Collection,從新渲染。最好的操做方式是隻實例化一個過濾後的Collection,當這個Collection改變的時候渲染對應的filterView
。
var FilteredView = Backbone.View.extend({ initialize: function(){ this.listenTo(this.collection, "reset", this.render, this); } }); var filteredCollection = new Backbone.Collection(); var filteredView = new FilteredView({ collection: filteredCollection }); $("#run").click(function(e){ var filter = { // ... new filters }; var results = myCollection.where(filter); filteredCollection.reset(results); });
Addition
加一點underscore的東西。
underscore的invoke
方法是個很好用的方法,來看個栗子:
var demoCollection = Backbone.Collection.extend({ model: demoModel, selected : function() { return this.filter(function(itm) { return itm.get('selected'); }); } }); var selected = demoCollection.selected();
上文中我已經講了如何把filter
以後的返回數組變成Backbone.Collection的實例,可是很明顯,這裏返回的是數組。
下面的狀況通常發生在批量操做的時候,如批量刪除,咱們要把所選的批量刪除掉,咱們可能須要訪問一個api,而後把id傳過去。
若是selected
返回的是Backbone.Collection實例對象,咱們能夠直接使用pluck
方法:
var ids= selected.pluck('id'); //返回id的數組
若是像上面selected
只是Model數組呢?如何取到id的數組呢?
你可能經過each
循環:
var ids = []; _.each(selected, function(itm){ ids.push(itm.get('id')); });
若是你想返回的不僅是id的數組的話,each
方法其實很適合,若是就想返回一項的數組的話,其實還有更簡單的方法。
若是你有去看看Backbone的源碼中的pluck
方法,你會發現這方法其實調用的是underscore的invoke
方法,所以,so easy:
ids = _.invoke(selected, 'get', 'id');
那你要從Collection批量刪除的話,你也能夠直接:
_.invoke(selected, 'destroy'); //http://underscorejs.org/#invoke _.invoke(list, methodName, *arguments)
所以後面的參數也能夠傳多個,可是返回的是一個數組。
其實再看看underscore的源碼的話,invoke
方法內部調用的_.map
,因此也能夠用map
方法去實現咯。