Backbone.js学习记录-model
- Time: 10 May 2014
- Categories:
- other 11
借用backbone官方的一句话“Model是任何JS应用中最核心的部分”,包含了围绕该数据一些逻辑,比如:数据验证、转换、属性计算和访问控制。它是一个抽象的数据模型,相当于数据表中的一条数据。它的另外一个功能就是用于做持久化,即把数据存储到硬盘(文件、数据库形式)。在web端即对应存入localStorage等。当然,和服务器进行数据也是一个非常主要的功能。
###创建Model
创建模型类的时候,可以覆盖已有的构造函数(一般时候我们不这么做)。通过给defaults
属性赋值即可传入默认值,可以直接传入一个对象值,也可以传入一个返回对象的函数。由于对象是传址对象,所以建议尽量使用返回对象的函数。模型在实例化的时候自动调用initialize方法。
var Child = Backbone.Model.extend({
defaults: function () {
return {
name: 'Jim'
,age: 10
} ;
}
});
//实例化模型
var Jack = new Child({
name: 'jack'
,age: 12
,fun: '<b>吃饭</b>'
});
###读取数据
通过数据模型的get方法读取数据,如果包含实体数据,可以使用escape来获取(escape会自动将转义内容,可以防止xss攻击),数据的attributes对象属性包含了所有的属性,不建议直接操作该属性,但可以在console中打印用以debug。
//获取数据
console.log(Jack.attributes) ;
console.log(Jack.get('name')) ;
//escape entities
console.log(Jack.escape('fun')) ;
###写入数据
可以通过set方法来设置数据,设置数据时会触发change事件,通过监听change事件,我们可以对页面的UI进行及时的调整,change回调中传入的是修改后的数据。我们还可以对单个属性进行监听,当被监听的属性发生改变时,对应的属性事件(格式是change:attr)就会被触发。如果数据同时有事件监听和属性事件监听,属性事件总是先被触发。
//修改数据
Jack.set({
//jack 长大了一岁
age: 13
//不再是个吃货了
,fun: 'football'
})
console.log(Jack.attributes) ;
//事件监听
Jack.on('change', function (model, value) {
console.log('model has been changed, new value is :', value) ;
});
//还可以单独对某个属性进行监听,属性事件总是先被触发
Jack.on('change:age', function (model, value) {
console.log('model has been changed, new age is :', value) ;
});
值得注意的是,bakcbone允许我们在修改数据模型时,获取上一次状态的数据,这就要用到previous 或 previousAttributes属性了。但一般情况下,我们只能在监听事件的回调过程中能获取到数据的上一个状态。(如果设置数据时附加了silent:true的选型,则又是另一种情况)
Jack.on('change:age', function (model, value) {
console.log('修改前的value是:', model.previous('age')) ;
console.log('修改后的value是:', model.get('age')) ;
});
Jack.set({ age: 15 });
###删除数据
- unset用于删除单个属性
- clear用于删除数据的所有属性
-
destroy方法用于将数据从集合和服务器中删除,需要注意的是,该方法并不会清除模型本身的数据。
//删除数据 Jack.unset(‘age’); console.log(‘unset age’, Jack.attributes) ; Jack.clear(); console.log(‘clear all attr’, Jack.attributes) ;
###数据验证
根据项目需求来定义validate方法和返回值,validate的验证规则是:如果验证通过则返回undefined;如果不通过,则返回自定义错误,并触发invalid事件(可以简单理解为:只要不返回undefined,就触发invalid)。可以预先监听模型的invalid事件来捕获这些错误,并反馈到UI层。
值得注意的是:设置数据时默认是不会触发validate,触发有弃用验证选型。而在数据发生变化(比如save)时会自动触发validate。
var Book = Backbone.Model.extend({
validate: function (data) {
if (data.price < 1) {
return 'price is unavailable' ;
}
}
});
var jsBook = new Book( {price: 5});
jsBook.on('invalid', function (model, error) {
console.log('model data invalid .', error) ;
})
//set时需要手动指定validate参数才行
//jsBook.set({price: 0.5}, {validate:true});
//save自动触发validate
jsBook.save({price: 0.5});
###数据同步和url规则
Backbone提供了与服务器数据的无缝连接,我们只需要操作本地Model对象,Backbone就会按照规则自动将数据同步到服务器。如果使用默认的同步特性,请保证服务器数据接口支持rest架构。在rest架构中,数据的CRUD直接通过请求头中的request method来传达。
每个模型对象都有一个数据标识——id(可以通过idAttribute来修改),来与服务器的交互中,模型会自动在url后加上id。(新建时不会加。可以通过isNew来判断)
- url,固定地址,我们无需让Backbone自动连接模型id(这可能是在url本身已经设置了模型id,或者不需要传递模型id)
- urlRoot,表示服务器接口地址的根目录,我们无法直接访问它,只能通过连接模型id来组成一个最终的接口地址(在删除数据时需要使用urlRoot,因为需要backbone将model的id追加的url中)
先创建一个英雄,接下来逐步来介绍数据的CRUD。
var Girl = Backbone.Model.extend({
urlRoot: 'http://localhost:8080/hero/'
});
var katarina = new Girl({
name: 'kata'
,age: 18
});
####create
//create
katarina.save(null, function(){
console.log('saved success');
});
如果数据没有id属性(模型内部会通过isNew来检测),则表示是新增——creat,对应的request method是POST。可以在浏览器network面板中看到request header。同时对象的属性也会通过request payload传输到服务端。
如果服务器的respond body中没有任何数据,你会发现保存之后的模型较之前没有发生任何变化,在你下一次调用save()方法的时候,它仍然会以POST方式通知服务器创建一条新的数据。这是因为模型对象并没有获取到刚刚服务器创建成功的记录id,因此我们希望服务器接口在将数据保存成功之后,同时将新的id返回给我们,如:
{
"id" : "1001",
"name" : "kata",
"age" : "18"
}
这一段是服务器接口返回的数据,它除了返回新记录的id,(当然,你也可以只返回新记录的id,我们常常都是这样做的)。这时我们再来看现在模型中的数据,它多了一个id属性,也就是说模型使用服务器返回的最新数据替换了之前的数据。
在配置项中,还可以包含一个wait配置,如果我们wait配置为true,那么数据会在被提交到服务器之前进行验证,当服务器没有响应新数据(或响应失败)时,模型中的数据会被还原为修改前的状态。如果没有传递wait配置,那么无论服务器是否保存成功,模型数据均会被修改为最新的状态、或服务器返回的数据。接上面的代码调整:
katarina.save({
name : 'kata'
,age : 20
}, {
wait : true
});
console.log(katarina.attributes);
此时,将服务端返回的数据设置为空。你能看到在save()调用完成之后,模型中的数据被恢复成最初定义的数据,因为我们在调用save()方法时传递了wait配置。(你也可以试着将wait配置去掉,然后再运行它,你会发现虽然服务器接口并没有返回数据或保存成功,但模型对象中仍然保持着最新的数据)
上面提到服务器接口返回的数据会被覆盖到当前模型中,在刚刚的例子里,接口返回的数据就是模型需要的数据。而实际开发中往往并没有这么顺利,我们接口返回的数据可能不是我们期待的json格式。这样一来,我们就需要自定义parse方法来解析返回的数据
var katarina = Backbone.Model.extend({
urlRoot : 'server',
// 重载parse方法解析服务器返回的数据
parse : function(resp, xhr) {
var data = resp.data[0];
return {
id : data.id,
age : data.age
}
}
});
另外,建议success回调函数中只要做一些与业务逻辑和数据无关的、单纯的界面展现即可,如果数据保存成功之后涉及到业务逻辑或数据显示,你应该通过监听模型的change事件,并在监听函数中实现它们。虽然Backbone并没有这样的要求和约束,但这样更有利于组织你的代码。
####update
//update
katarina.save({id:123});
//backbone会追加id到url中,所以对应的请求URL是 http://localhost:8080/hero/123
数据中带有id(可通过isNew来检测是否是新数据),表示是update数据,对应的request method是PUT.
####read
//read
katarina.fetch({id:123});
fetch表示从服务端获取数据,此时,对应的request method就是GET
####delete
//delete
katarina.set({id:111});
katarina.destroy({});
destroy表示从服务端删除数据,此时,对应的request method是DELETE
在调用destroy()方法时我们同样可以传递一个配置对象,它除了success和error回调函数外,也能像save()方法一样包含一个wait配置,来看下面的例子:
katarina.on('destroy', function(){
console.log('be destroied') ;
})
katarina.destroy({wait: true});
如果服务器没有正确的返回,那么destroy事件是不会触发的。
###一句话小结
上面啰啰嗦嗦的说了关于数据的创建,读取,验证,同步,以及其中的一些细节。接下来继续攻略collection。