本章主要介绍使用Backbone 对 WebAPI 进行CRUD,我们将会操作一个数据集(比如:留言簿里的留言一览)。对于数据集合,Backbone 里有专门的类型—— Backbone.Collection。对于集合的管理,Backbone.Collection 在创建后有 add, remove 事件,子元素的 update 需要自己在 model 上监听 "change" 事件。根据初始化的 Backbone.Collection.url,Collection会自动为子元素分配 url/:id 这样的URL,所以 Model 里应该定义 id 属性。另一方面,服务端的 WebAPI结构也需要符合规范。
整体示意图如下:
这里将一条Comment的View拆分出来,CommentListView 将 fetch 数据再挨个创建出 CommentView 。CommentListView 响应 UI 的删除和添加操作。
CommentView 和 Comment Model:
var CommentModel = Backbone.Model.extend({
idAttribute: "ID",
urlRoot: 'api/comments'
});
var CommentView = Backbone.View.extend({
el: '',
template: _.template($('#commentTemplate').html()),
initialize: function () {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
render: function() {
var data = this.model.toJSON();
this.$el.html(this.template(data));
return this;
},
remove: function() {
this.$el.remove();
}
});
注意:delete 按钮按下处理时,需要知道处理的是哪一个 Model,因此在Template 生成时,将 CommentModel 的 ID 属性保存到 HTML 里了。
<script id="commentTemplate" type="text/html">
<li class="comment">
<header>
<div class="info">
<img src='<%= GravatarUrl %>' />
<strong><span><%= Author %></span></strong>
</div>
<div class="actions">
<a class="delete" href="#" id='<%= ID %>'>Delete Id: <span><%= ID %></span></a>
</div>
</header>
<div class="body">
<p><%= Text %></p>
</div>
</li>
</script>
对于一览的控制:CommentList 则继承于 Backbone.Collection,由 CommentListView 处理 delete 和 submit 按钮事件:
CommentList.model.models 就是 CommentModel 的集合。
var CommentList = Backbone.Collection.extend({
url: 'api/comments',
model: CommentModel
});
var CommentListView = Backbone.View.extend({
el: 'body',
initialize: function () {
this.model.on('reset', this.render, this);
},
events : {
'click .delete': 'delete',
'click .submit': 'create'
},
render: function () {
var self = this;
$('#comments').empty();
_.each(this.model.models, function(mdl) {
var commentView = new CommentView({model: mdl});
$('#comments').append(commentView.render().$el);
});
},
delete: function(obj) {
alert('delete: ' + $(obj.currentTarget).attr('id'));
var id = parseInt($(obj.currentTarget).attr('id'), 10);
var deletedModel = _.find(this.model.models, function(item) {
return item.get('ID') === id;
});
var self = this;
deletedModel.destroy(
{
success: function() {
self.model.remove(deletedModel);
},
error: self.errorHandle
});
},
create: function() {
var metaData = {
ID: null, Text: $('#text').val(),
Author: $('#author').val(), Email: $('#email').val(),
GravatarUrl: ''
};
var comment = new CommentModel(metaData);
var self = this;
comment.save(metaData,
{
success: function(res) {
console.log(JSON.stringify(res));
self.model.add(res);
var view = new CommentView({model: res});
$('#comments').append(view.render().$el);
},
error: self.errorHandle
});
},
errorHandle: function(model, xhr, options) {
var err = $.parseJSON(xhr.responseText);
alert(err.ExceptionMessage);
}
});
按照 Backbone 的定义,Model 会从 Collection.url 里“继承” URL,然后加上 id 属性。但是如果新创建的 Model(add 操作),这个新Model对应URL就无法取得了。因此可以看到上面 CommentModel 的代码中加了 urlRoot 属性,保证新建的 Model 也能映射到正确的 URL 上。说到这里,Backbone 和 Knockout 比较起来,需要写更多的代码,但也更加灵活。Backbone 里的 Backbone.sync 封装了 AJAX 的逻辑。而 Knockout 需要自己去调用ajax 了。