Model
View的部分基本上就是这样了,现在开始来谈Model。
Model其实就是一条数据记录。只不过它不是一般的静态数据记录,它不但可以不限定数据结构,还可以自动与后端交互,甚至还可以绑定事件以实现相应View的自动更新。
最简单的Model就是这样:
var Foo = Backbone.Model.extend({});
// 或是初始化默认数据
var Foo = Backbone.Model.extend({
defaults: {
name: "hello",
value: "233"
}
});
// 或是运行时赋值。
// 比如这样:
var foo = new Foo({name:"world", value:"874"});
// 或这样:
var foo = new Foo;
foo.set({name:"world", value:"874"});
可以显示一下结果看看效果:
$("#body").text(JSON.stringify(foo.toJSON()));Collection
Collection就是一个Model集合。因为Model是一条数据记录,也就是说,Collection相当于是一个数据集。
同样,一个最简单的Collection如下:
var FooList = Backbone.Collection.extend({
model: Foo
});
之后你可以往里增加数据:
var foos = new FooList;
foos.add([{name:"hello", value:"233"},{name:"world", value:"874"}]);
$("#body").text(JSON.stringify(foos.at(0)));
更新删除的方式请参考官方文档。
注意,每个Model记录会自动有一个id/cid的属性,是记录的唯一标志。比如:
foos.get(foos.at(0).cid)
Model的事件绑定
为了能在数据变更之后及时更新View上的显示,那就需要通过事件机制来处理。
var Task = Backbone.Model.extend({
initialize: function () {
this.on("change:name", function (model) {
alert("new name is : " + model.get("name"));
});
}
});
var t = new Task({tid:"3333", name:"oooo", state:"working"});
t.set({name:"xxx"});当执行到t.set()的时候,Model数据发生变化,将触发change:name事件。
测试用的后端
在介绍与后端交互之前,先来一个简单的后端。
这里用web.py做一个最简单的:
# start.py
import web
web.config.debug = False
from webpyext.webcommon import WebNotfoundError
from webpyext.apiserver import RestBaseHandler, kwargs_decorator
import logging
logger = logging.getLogger(__name__)
urls = (
"/task/?" , "Tasks",
"/task/([0-9]+)/?" , "Tasks",
)
app = web.application(urls, locals())
class APIHandler(RestBaseHandler):
def __init__(self):
self.dbconn = web.database(dbn='sqlite', db='xllxweb.dat')
class Tasks(APIHandler):
def GET_(self, id=""):
if id:
result=self.dbconn.select("task", where="id=$id", vars={'id':int(id)}).list()
if not len(result):
raise WebNotfoundError("This task was not found!")
return result[0]
else:
return self.dbconn.select("task").list()
@kwargs_decorator
def POST_(self, kwargs={}):
return "%s" % self.dbconn.insert("task", **kwargs)
@kwargs_decorator
def PUT_(self, id, kwargs={}):
self.dbconn.update("task", where="id=$id", vars={'id':id}, **kwargs)
return ""
def DELETE_(self, id):
self.dbconn.delete("task", where="id=$id", vars={'id':id})
return ""
if __name__ == "__main__":
import logging
logging.basicConfig(level=logging.DEBUG)
dbconn = web.database(dbn='sqlite', db='xllxweb.dat')
sql = """create table if not exists task (
id integer primary key not null,
name varchar not null,
state varchar
)"""
dbconn.query(sql)
app.run()
其中 webpyext 是我自己写的一个扩展 web.py 的工具库,提供一些增强功能。比如这里用到的三个东西:RestBaseHandler, kwargs_decorator, WebNotfoundError。
其 中RestBaseHandler是提供一个统一的route handler去处理API调用,把固定格式的REST API调用请求转为对方法的调用,其中还包括了对返回数据的JSON转换。kwargs_decorator用于把HTTP请求参数转为方法调用中的kwargs参数,可以自动处理backbone的JSON格式请求(不是一般的HTTP标准请求)。WebNotfoundError是一组HTTP响应专用的异常之一。
本例使用一个SQLite数据库:xllxweb.dat 作为测试,其中只包含一个表:task ,表结构见代码。
现在这个后台实现了这些功能:
/task : 取得所有tasks(GET)或创建一个新task(POST,带参数为task字段,返回记录id号)
/task/[:tid] : 取得某个TaskID的task(GET)或修改(PUT,参数为需要修改的字段)或删除(DELETE,无参数)
以上完全是按照backbone的要求实现,并符合REST规范。
运行 python start.py 即可通过 http://localhost:8080 访问。
Model/Collection与后端交互
首先是写相应的Model和Collection:
var Task = Backbone.Model.extend({
urlRoot: "/task",
});
var TaskList = Backbone.Collection.extend({
url: "/task",
model: Task,
});就是最简单的实现,与前面的例子相比,就是增加了url/urlRoot,这就是与后端交互的机关所在。
之后就可以在这个基础上进行CRUD操作了。
// Create
var t = new Task({name:"hello", state:"waiting"});
t.save();
alert("Saved");
var tid = t.id
// Read
t = new Task({id:tid});
t.fetch({success: function(task) {
alert(JSON.stringify(task.toJSON()));
}});
// Update
t = new Task({id:tid});
t.set({name:"world"});
t.save();
alert("Updated");
// Delete
t = new Task({id:tid});
t.destroy();
需要注意的是:这些所有的操作都是异步的,如果需要获得操作结果(比如Read操作),必须通过回调函数(比如fetch里那个success回调),否则通常将无法取得正确的结果(比如在fetch执行后立即读取t变量的值,将会只有id一个值,其它的属性值都会为空)。代码中插入的alert主要是起等待作用。
以上是Model的操作方式,Collection与此类似,区别只在于Collection取得的是一组Model列表。
关于以上操作对应的HTTP请求及响应格式如下:
| Model Request | Model Response | Collection Request | Collection Response | |
| Create | POST /task/ Content-Type: application/json {tid: "1234"...} | ... | No | No |
| Read | GET /task/1 | {tid: "1234"...} | GET /task/ | [{tid: "1234"...}...] |
| Update | PUT /task/1 Content-Type: application/json {name: "world"...} | ... | No | No |
| Delete | DELETE /task/1 | ... | No | No |
上表供进行后端开发时参考。如开发中遇到任何问题,建议立即打开FireBug分析。
其中Collection只有Read,其它的Create/Update/Delete其实最终都是调用Model的相应方法处理的。
Model的数据校验
为了防止往Model里存入不正确的值,可以对其加一个校验检查,类似于数据库的“约束(constraint)”。
下面的代码约束name字段必须长于3个字符。
var Task = Backbone.Model.extend({
urlRoot: "/task",
initialize: function () {
this.on("invalid", function (model, error) {
alert(error);
});
},
validate: function (attrs, options) {
if (attrs.name.length<3) {
return "name must longer than 3 chars!";
}
},
});
var t = new Task({id:1});
t.fetch({success: function (task) {
task.set({name:"hi"});
task.save();
}
});注意,backbone有个坑爹的设定,那就是如果set一个字段为空,比如 set({name:""}) 时将不会触发validate校验,在实际应用中要小心这个坑。
除此之外,还有一个坑就是:在我现在用的这个版本(0.9.10)里,validate只在save的时候触发,网上很多资料说它会在set的时候触发可能是以前版本的设定,这个坑也要小心。
(待续)
本文详细介绍了Backbone.js中的Model和Collection,包括它们的数据结构、与后端的交互、事件绑定以及数据校验。Model是单条数据记录,支持自定义数据结构、自动与后端交互和事件绑定。Collection则是一个Model的集合,提供了添加、删除等操作。文中还通过示例演示了如何使用Model和Collection与一个基于web.py的后端进行CRUD操作,并讲解了Model的数据校验机制,以确保数据正确性。
1万+

被折叠的 条评论
为什么被折叠?



