集合是模型的组合,可以通过扩展Backbone.Colleciton来创建集合。
通常情况下,创建集合的时候也要定义一个属性,指定该集合所包含的模型(model)类型,同时还包含任何所需的示例属性。
一个简单的示例,创建一个包含MyModel模型的集合MyCollection集合:
//创建一个模型
var MyModel = Backbone.Model.extend({});
//创建一个集合
var MyCollection = Backbone.Collection.extend({
model:MyModel
});
var myModel = new MyModel();
var myCollection = new MyCollection([myModel]);
console.log("collection size : " + myCollection.length); //collection size : 1
添加和移除模型
在上面的简单示例中,集合在实例化过程中使用包含模型的数组来进行集合的填充。集合创建以后,可以通过使用add()和remove()方法进行添加或移除模型。
var MyModel = Backbone.Model.extend({});
var MyCollection = Backbone.Collection.extend({
model:MyModel
});
var m1 = new MyModel({'title':'第一个模型'}),
m2 = new MyModel({'title':'第二个模型'}),
m3 = new MyModel({'title':'第三个模型'});
var myCollection = new MyCollection([m1,m2]);
console.log("Collection size " + myCollection.length);//Collection size 2
myCollection.add(m3);
console.log("Collection size " + myCollection.length);//Collection size 3
myCollection.remove([m1,m2]);
console.log("Collection size " + myCollection.length);//Collection size 1
myCollection.remove(m3);
console.log("Collection size " + myCollection.length);//Collection size 0
add()和remove()方法都支持传入单个模型和模型数组。
另外,在集合上使用add()方法时,传入{merge:true}参数(不指定时默认为false),将导致新模型合并到现有模型上,而不是忽略。
var items = new Backbone.Collection;
items.add([{id:1, name:'A',age:3}, {id:2, name:'B',age:2}]);
items.add([{id:1, name:'C'}],{merge:true});
items.add([{id:2, name:'D'}]);
console.log(JSON.stringify(items.toJSON()));
//[{"id":1,"name":"C","age":3},{"id":2,"name":"B","age":2}]
检索模型
有集中不通的方法可以从集合中检索一个模型,最简单的方法是使用集合的.get()方法,接受一个id作为参数,代码如下:
var MyModel = Backbone.Model.extend({});
var MyCollection = Backbone.Collection.extend({
model:MyModel
});
var myModel = new MyModel({id:2,title:'一个模型'});
var myCollection = new MyCollection([myModel]);
var myModel2 = myCollection.get(2);
//模型是一个对象,是引用传递
console.log(myModel2===myModel); //true
在Backbone里,可以使用id、cid和idAttribute属性来定义唯一标识,用来表示模型。每个模型都有一个id,他是唯一标识符,可能是整数,也可能是字符串(例如:UUID)。模型也有一个cid(客户端ID),在模型创建的时候有Backbone自动生成。两种标识符都可以从集合里检索模型。
id和cid的主要 区别是:cid是由Backbone生成的,如果我们没有真正定义id,那么我们就可以用cid来确定模型的唯一性。
idAttribute是从服务器端所返回模型的标识属性。该属性告诉Backbone服务器上的哪个数据字段用于填充id属性。默认情况下,它假定为id,我们可以根据实际情况进行定义。例如服务器在模型上设置的唯一属性叫userId,那么在模型定义的时候,就需要就将idAttribute设置为userId。
如果模型实例有值,Backbone.Collection在内部会包含一个可以用id属性遍历的模型数组。调用collection.get(id)时,该数组会检查与模型id像一只的模型实例是否存在。
接着上面的例子:
var myModel3 = myCollection.get(myModel2.cid);
console.log(myModel3 === myModel); //true
事件监听
因为集合标识的是一组item,所以我们可以监听add和remove事件,从集合添加或移除模型的时候会触发这些事件,示例如下:
var MyCollection = new Backbone.Collection();
MyCollection.on("add",function(model){
console.log("名字为[" + model.get('name') + "]的模型被添加到集合中,属性desc为:" + model.get("desc"));
});
MyCollection.add([
{name:'A',desc:'我是A模型'},
{name:'B',desc:'我是B模型'},
{name:'E',desc:'我是模型E'}
]);
//Log信息
名字为[A]的模型被添加到集合中,属性desc为:我是A模型
名字为[B]的模型被添加到集合中,属性desc为:我是B模型
名字为[E]的模型被添加到集合中,属性desc为:我是模型E
此外,在集合中的任意模型的属性上,都可以绑定change事件,用于监听这些模型属性的变化。
var MyCollection = new Backbone.Collection();
MyCollection.on("change:name", function(model){
console.log("模型的名字被修改为:" + model.get("name"));
});
MyCollection.add([
{id:3,name:"C",desc:"我是模型C"}
]);
var myModel = MyCollection.get(3);
myModel.set("name","CCC");
//Log信息:模型的名字被修改为:CCC
jQuery风格的事件映射形式obj.on({click:action})也可以使用,这比在.on上使用3个单独的调用要清晰。示例:
var MyModel = Backbone.Model.extend({});
var myModel = new MyModel();
myModel.set({name:'A',desc:"我是模型A"});
myModel.on({
'change:name':nameChanged,
'change:desc':descChanged
});
function nameChanged(){
console.log('名字被修改');
}
function descChanged(){
console.log('描述信息被修改');
}
myModel.set({name:'B'});
//log信息:名字被修改
Backbone事件还支持once()方法,它可以确保在通知到达后,回调只执行一次。它类似于Node的once或者jQuery的one。当我们想“下一次发生时,调用这个”时,这样做就非常有用。
var MyCounter = {counterA:0, counterB:0};
_.extend(MyCounter,Backbone.Events);
var incrA = function(){
MyCounter.counterA += 1;
MyCounter.trigger('event');
}
var incrB = function(){
MyCounter.counterB += 1;
}
MyCounter.once('event', incrA);
MyCounter.once('event', incrB);
MyCounter.trigger('event');
console.log(MyCounter.counterA === 1);//true
console.log(MyCounter.counterB === 1);//true
counterA和counterB应该只增加一次。
重置和刷新集合
比起一个一个单独添加或删除模型,我们更想要一次性更新整个集合,Backbone.set()接收一个模型数组参数,并执行更新集合所必要的添加、删除和更新操作。
var MyCollection = new Backbone.Collection();
MyCollection.add([
{id:1, name:'A', desc:'我是模型大A'},
{id:2, name:'B', desc:'我是模型大B'},
{id:3, name:'C', desc:'我是模型大C'}
]);
MyCollection.on({
"add":function(model){
console.log('增加了一个模型,名字为'+model.get('name'));
},
"remove":function(model){
console.log('删除了一个模型,名字为'+model.get('name'));
},
"change:desc":function(model){
console.log('名字为'+model.get('name')+'的模型描述信息发生改变。')
}
});
MyCollection.set([
{id:1, name:'A', desc:'我是模型小a'},
{id:2, name:'B', desc:'我是模型大B'},
{id:4, name:'D', desc:'我是模型大D'}
]);
//Log信息:
//名字为A的模型描述信息发生改变。
//删除了一个模型,名字为C
//增加了一个模型,名字为D
如果需要见到那地替换整个集合的内容,可以使用Collection.reset():
var MyCollection = new Backbone.Collection();
MyCollection.on("reset", function(){
console.log("集合被重置");
});
MyCollection.add([
{name:'A'},
{name:'B'},
{name:'C'}
]);
console.log("Collection size: " + MyCollection.length); //Collection size: 3
//使用集合的reset方法
MyCollection.reset([
{title:'X'}
]);
//Log信息:集合被重置
console.log("Collection size: " + MyCollection.length); //Collection size: 1
reset方法还可以使用不带参数的方法来完全清空一个集合,当给我们想清空当前页面的记录,动态加载新纪录是可以这样使用
myCollection.reset();
注意在使用reset()方法的时候,不会触发add和change事件,而是触发reset事件。我们使用它的原因是要在极端情况下使用超级优化的渲染方式,因为单独的触发事件太浪费资源。
Underscore实用函数
Backbone充分利用了其强依赖JavaScript库Underscore,使得很多实用函数都可以直接在集合上使用。
链式API
链式是面向对象语言的一个常见用法,链就是通过一条语句在同一个对象上进行一组方法的调用。当Backbone在让Underscore的数组操作行为可以在集合上使用时,并不能直接进行链式操作,因为他们返回的是数组,而不是原有集合。但是Underscore的chain()方法可以让我们在集合上进行链式调用。
chain()方法返回一个对象,并且Underscore的所有数组操作方法都附加到该对象上。在链的结尾通过调用value()方法返回数组结果。如果你之前没有见过链式API,它看起来就像这样:
var collection = new Backbone.Collection([
{name:'A',age:5},
{name:'B',age:4},
{name:'C',age:6}
]);
var filterdNames = collection.chain()
.filter(function(item) {return item.get('age')>=5;})
.map(function(item) { return item.get('name');})
.value();
console.log(filterdNames); //["A", "C"]
Backbone的一些特定方法返回this,也就意味着他们也可以链式操作:
var collection = new Backbone.Collection();
collection
.add({name:'A',age:5})
.add({name:'B',age:4})
.add({name:'C',age:6});
var names = collection.pluck('name');
console.log(names);//["A", "B", "C"]