Backbone基础(三):集合(Collection)

/ 0评 / 0

集合是模型的组合,可以通过扩展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"]

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注