当前位置: 代码迷 >> JavaScript >> 基于Backbone.js的JavaScript MVC示范程序(6)
  详细解决方案

基于Backbone.js的JavaScript MVC示范程序(6)

热度:225   发布时间:2013-10-30 12:56:21.0
基于Backbone.js的JavaScript MVC示例程序(6)
  • 一.概述
  • 二.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 吧。

  相关解决方案