首先我想说的是这篇文章的题目起的很怪,由于我不知道起个什么名字比较好。渲染列表是咱们应用中最多见的操做了吧,在运用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
方法去实现咯。