- 一.概述
- 二.REST Server的实现
- 2.1 REST API设计
- 2.2 数据库设计
- 2.3 用MyBatis实现的DAO层
- 2.4 用Jersey实现的REST API
- 2.5 用Spring AOP实现的日志功能
- 三.前端的实现
- 3.1 显示User列表
- 3.2 显示User详细信息
- 3.3 修改User信息
- 3.4 增加User
- 3.5 删除User
- 3.6 添加validate
3.3 修改User信息
界面如下,在右边 User 详细信息处增加了一个 "Edit" 按钮,点击之后下面的信息就变成了输入框,点击 "Submit" 完成修改,左侧列表随之更新。
想要实现点击"Edit"将右侧详细信息的变为可编辑有几种方法:
1. 创建一个 UserEditView 专门来显示修改User信息的表单;
2. 点击 "Edit" 之后,将 UserInfoView 所绑定的 HTML 模板换成一个表单;
3. Todos 这个例子里面给出了另外一种思路,它在 HTML 模板里面隐含了一个表单,默认情况下通过 css 将表单隐藏,点击 "Edit" 之后显示表单,并将非表单部分隐藏。
本例中采用了第三种方法。
3.html,在 user-info-template 模板中加入一个隐含的表单:
1 <!-- user-info-template中加入编辑User信息的表单 --> 2 <script type="text/template" id="user-info-template"> 3 <h3>User Information</h3> 4 <button id="edit">Edit</button> 5 <ul id="user-info"> 6 <li>ID:<span><%= id %></span></li> 7 <li>Username:<span><%= username %></span><input type="text" name="username" value="<%= username %>" /></li> 8 <li>Password:<span><%= password %></span><input type="password" name="password" value="<%= password %>" /></li> 9 <li>Email:<span><%= email %></span><input type="text" name="email" value="<%= email %>" /></li> 10 <li>Phone:<span><%= phone %></span><input type="text" name="phone" value="<%= phone %>" /></li> 11 <button id="edit-submit">Submit</button> 12 </ul> 13 </script>
mvc3.js
1 $(document).ready(function() { 2 3 var User = Backbone.Model.extend({ 4 }); 5 6 var UserList = Backbone.Collection.extend({ 7 ... //不变 8 }); 9 10 var UserItemView = Backbone.View.extend({ 11 tagName : "li", 12 userItemTemplate : _.template($("#user-item-template").html()), 13 events : { 14 "click a" : "displayInfo", 15 }, 16 initialize : function() { 17 //this.infoView = new UserInfoView({model : this.model}); //删除 18 this.model.bind('change', this.render, this); //新增,修改成功后会触发 19 }, 20 render : function() { 21 this.$el.html(this.userItemTemplate(this.model.toJSON())); 22 return this; 23 }, 24 /*displayInfo : function() { 25 this.infoView.render(); 26 },*/ 27 displayInfo : function() { //修改,注释一 28 if(_.isEmpty(infoView)){ 29 infoView = new UserInfoView({model : this.model}); 30 }else{ 31 infoView.model = this.model; 32 infoView.model.unbind('change'); 33 infoView.model.bind('change', this.render, this); 34 infoView.model.bind('change', infoView.render, infoView); 35 } 36 infoView.render(); 37 }, 38 }); 39 40 var UserInfoView = Backbone.View.extend({ 41 el : $("#right"), 42 userInfoTemplate : _.template($("#user-info-template").html()), 43 events : { 44 "click #edit" : "displayEdit", //新增 45 "click #edit-submit" : "submitEdit", //新增 46 }, 47 initialize : function() { 48 this.model.bind('change', this.render, this); //新增 49 }, 50 render : function(){ 51 this.$el.html(this.userInfoTemplate(this.model.toJSON())); 52 return this; 53 }, 54 displayEdit : function() { //新增,改变 class,显示表单 55 this.$("#user-info").addClass("editing"); 56 }, 57 submitEdit : function() { //新增,提交表单 58 this.model.save({ //注释二 59 "username":$("input[name='username']").val(), 60 "password":$("input[name='password']").val(), 61 "email":$("input[name='email']").val(), 62 "phone":$("input[name='phone']").val(), 63 }); 64 this.$("#user-info").removeClass("editing"); //改变 class,隐藏表单 65 }, 66 }); 67 68 var UserListView = Backbone.View.extend({ 69 ... //不变 70 }); 71 72 var userListView = new UserListView(); 73 var infoView; //新增 74 });
注释一:
在上一节中,每一个 UserItemView 都与一个 UserInfoView 一一对应,在显示User详细信息的时候是没有问题的,但是在修改User信息的时候会出现问题。因为多个 UserInfoView 绑定到了同一个 "Submit" 按钮上,所以在点击 "Submit" 的时候可能会更新好几个 User 的信息。
解决的方法是声明一个全局的 UserInfoView(73行),在每一次点击列表中的 User 时,都将 UserInfoView 的 model 设置为当前点击的 User(31行),并且将当前的 UserItemView 和 UserInfoView 的 render() 方法绑定到这个 User 的 change 事件上(33-34行),并与之前的 User 解绑(32行),这样的话表单提交之后,左侧的列表和右侧的详细信息都会随之改变,而且也不会影响到其他 User。
不知道会不会有更好的方法,欢迎交流。
注释二:
官网上对 model.save() 方法的解释如下:
model.save([attributes], [options])
通过委托 Backbone.sync 保存模型到数据库(或可替代的持久层)。 attributes 散列表 (在 set) 应当包含想要改变的属性,不涉及的键不会被修改。 如果模型含有 validate 方法,并且验证失败,模型不会保存。 如果模型 isNew, 保存将采用 "create" (HTTP POST) 方法, 如果模型已经在服务器存在,保存将采用 "update" (HTTP PUT) 方法。
进一步看一下 isNew() 方法的解释:
model.isNew()
模型是否已经保存到服务器。 如果模型尚无 id,则被视为新的。
在这里,被修改的 User 已经有 id 了,因此调用 model.save() 的时候自动向服务器发出 PUT 请求,请求中包含除了 id 之外的4个 attributes。
Model 有一个 id,还有一个 cid,官方文档是这么说的:
- model.id 模型的特殊属性, id 可以是任意字符串(整型 id 或 UUID)。 在属性中设置的 id 会被直接拷贝到模型属性上。 我们可以从集合(collections)中通过 id 获取模型,另外 id 通常用于生成模型的 URLs。
- model.cid 模型的特殊属性,cid 或客户 id 是当所有模型创建时自动产生的唯一标识符。 客户 ids 在模型尚未保存到服务器之前便存在,此时模型可能仍不具有最终的 id, 客户 ids 的形式为:c1, c2, c3 ...
也就是说 id 需要我们设置好,url() 方法会根据 id 来生成 Model 的 URL,这个 id 需要与 Server 端的 id 对应上,这样才能完成 GET/PUT/DELETE 请求。而 cid 我们并不需要去管它,实际上我们可以发现 View 也是有 cid 的。
之前在 Server 端返回的 JSON 中,User 的信息中正好有个 id,我们试一试将 JSON 中的 id 换成 uid,会有什么反应。修改起来比较简单,只要把 com.demo.register.bean.User 的 id 属性改成 uid 就行了。我们可以发现,在页面提交 Edit 的表单之后,会发出 POST 请求,并新增一个 User,而不是修改了当前 User。再进一步查看 userList,发现里面的 User 都没有 id,所以 isNew() 的返回结果是 false,save() 的时候一直都是发出 POST 请求。
通过重载 url() 和 isNew() 方法可以解决这个问题,只要将对 id 的判断改成对 uid 的判断就行了:
1 // Default URL for the model's representation on the server -- if you're using Backbone's restful methods, override this to change the endpoint that will be called. 2 url: function() { 3 var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError(); 4 if (this.isNew()) return base; 5 return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); 6 }, 7 //重载之后: 8 url: function() { 9 var base = this.collection.url ; 10 if (this.isNew()) return base; 11 return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.uid); 12 }, 13 14 // A model is new if it has never been saved to the server, and lacks an id. 15 isNew: function() { 16 return this.id == null; 17 }, 18 //重载之后: 19 isNew: function() { 20 return this.attributes.uid == null; 21 },
当然这只是一个实验,最好还是不要去重载它,老老实实地使用 id 吧。