本章主要介绍使用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 了。