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

来源:互联网 发布:网络pos机套现安全吗 编辑:程序博客网 时间:2024/04/29 20:02
  • 一.概述
  • 二.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 吧。