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

3.4 增加User

左侧 User 列表上方添加了一个 "Add" 按钮,点击之后右侧的页面会显示一个增加 User 的表单,表单提交之后弹出一个提示框,左侧列表也会更新。如下图所示:

4.html,新增了表单的模板和 "Add" 按钮:

 1 <!-- 增加User的表单 -->
 2 <script type="text/template" id="user-form-template">
 3     <h3>Add User</h3>
 4     <ul id="user-form" class="editing">
 5         <li>Username:<input type="text" name="username" /></li>
 6         <li>Password:<input type="password" name="password" /></li>
 7         <li>Email:<input type="text" name="email" /></li>
 8         <li>Phone:<input type="text" name="phone" /></li>
 9         <button id="add-submit">Submit</button>
10     </ul>
11 </script>
12 ......
13 <body>
14     <div id="main">
15         <div id="left">
16             <h3></h3>
17             <!-- 增加一个按钮,点击后显示增加User的表单 -->
18             <p><button id="add">Add</button></p>
19             <ul id="user-list"></ul>
20         </div>
21         <div id="right"></div>
22     </div>
23 </body>

mvc4.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         ... //不变
 12     });
 13     
 14     var UserInfoView = Backbone.View.extend({
 15         ... //不变
 16     });
 17     
 18     var UserListView = Backbone.View.extend({
 19         el : $("#main"),
 20         userFormTemplate : _.template($("#user-form-template").html()), //新增,绑定模板
 21         events : {
 22               "click #add" : "displayUserForm", //新增
 23               "click #add-submit" : "submitUserForm", //新增
 24         },
 25         initialize : function() {
 26             this.userList = new UserList();
 27             this.userList.bind('add', this.addOne, this); //新增,每当userList中加一个User时,列表中会加一个条目
 28             this.userList.bind('reset', this.addAll, this);
 29             this.userList.bind('all', this.render, this); 
 30             this.userList.fetch({silent: true, success:function(collection, response){ 
 31                 if(response != null){
 32                     collection.reset(response.user);
 33                 }else{
 34                     userListView.render();
 35                 }
 36             }});
 37             this.displayUserForm(); //新增,加载页面的时候默认显示增加User的表单
 38         },
 39         render : function() {
 40             this.$("#left h3").html("Total Number:"+this.userList.length);
 41         },
 42         addOne : function(user) {
 43             var view = new UserItemView({model : user});
 44             this.$("#user-list").append(view.render().el);
 45         },
 46         addAll : function() { 
 47             this.userList.each(this.addOne);
 48         },
 49         displayUserForm : function() { //新增,显示增加用户的表单
 50             this.$("#right").html(this.userFormTemplate());
 51         },
 52         submitUserForm : function() { //新增,提交增加用户的表单
 53             var user  = new User({
 54                 "username":$("input[name='username']").val(),
 55                 "password":$("input[name='password']").val(),
 56                 "email":$("input[name='email']").val(),
 57                 "phone":$("input[name='phone']").val(),
 58             });
 59 
 60             /**** 注释一 报错:id is not defined *****
 61             this.userList.create(user);
 62             $("input[name='username']").val(""),
 63             $("input[name='password']").val(""),
 64             $("input[name='email']").val(""),
 65             $("input[name='phone']").val(""),
 66             alert("Add a user!");
 67             *****************************************/
 68             
 69             /************** 注释二 正确 *************/
 70             this.userList.create(user,{wait:true});
 71             $("input[name='username']").val(""),
 72             $("input[name='password']").val(""),
 73             $("input[name='email']").val(""),
 74             $("input[name='phone']").val(""),
 75             alert("Add a user!");
 76             /****************************************/
 77             
 78             /**** 注释三 报错:A "url" property or function must be specified ****
 79             user.save({success : function(){
 80                 $("input[name='username']").val(""),
 81                 $("input[name='password']").val(""),
 82                 $("input[name='email']").val(""),
 83                 $("input[name='phone']").val(""),
 84                 this.userList.add(user);
 85                 alert("Add a user!");
 86             }});
 87             **********************************************************************/
 88             
 89             /************** 注释四 正确 *************
 90             var ul = this.userList;
 91             user.urlRoot = "/backbone-sample/rest/user";
 92             user.save({},{success : function(){
 93                 $("input[name='username']").val(""),
 94                 $("input[name='password']").val(""),
 95                 $("input[name='email']").val(""),
 96                 $("input[name='phone']").val(""),
 97                 ul.add(user);
 98                 alert("Add a user!");
 99             }});
100             *****************************************/
101         },
102     });
103     
104     var userListView = new UserListView();
105     var infoView; 
106 });

注释一:

如果使用第一段代码,会报错 “id is not defined”,让我们来找一下原因。

官网上对于 create() 方法的解释如下:
collection.create(attributes, [options]) 在集合中创建一个模型。等价于用键值对象实例一个模型,然后将模型保存到服务器,保存成功后将模型增加到集合中。如果验证失败会阻止模型创建,返回 false,否则返回该模型。为了能正常运行,需要在集合中设置 model 属性。create 方法接收键值对象或者已经存在尚未保存的模型对象作为参数。

也就是先执行 user.save(),再执行 this.userList.add(user)。很显然 save() 会发送一个 AJAX 请求,因此如果它是个异步请求,在执行 add() 方法的时候很可能还未得到响应,而 add() 触发了 userList 的 add 事件,于是会执行与之绑定的 addOne() 方法,在显示页面元素的时候需要读取 id,因此会报“id is not defined”。

注释二:

为了解决注释一中的错误,我们来看一下如何将 create() 方法的 AJAX 请求变成一个同步请求。

以下是 create() 方法的源码:

 1 // Create a new instance of a model in this collection. Add the model to the collection immediately, unless `wait: true` is passed, in which case we wait for the server to agree.
 2 create: function(model, options) {
 3     var coll = this;
 4     options = options ? _.clone(options) : {};
 5     model = this._prepareModel(model, options);
 6     if (!model) return false;
 7     if (!options.wait) coll.add(model, options);
 8     var success = options.success;
 9     options.success = function(nextModel, resp, xhr) {
10     if (options.wait) coll.add(nextModel, options);
11     if (success) {
12       success(nextModel, resp);
13     } else {
14       nextModel.trigger('sync', model, resp, options);
15     }
16     };
17     model.save(null, options);
18     return model;
19 },

很显然如果 options.wait 为 false 时会直接将 model 加到 collection 中(7行),而 options.wait 为 true 的时候会在回调函数 success 执行的时候才将 model 加到 collection 中(10行)。

因此解决方法很简单,就是在调用 create() 方法的时候加上参数 {wait:true}。

注释三:

注释一中已经分析过,执行 this.userList.create(user) 等价于先执行 user.save(),再执行 this.userList.add(user),因此我想到如果将 add() 方法的执行放到 save() 方法的 success 回调函数中,应该也能解决注释一中出现的错误。

但是注释三中的代码报出了 “A "url" property or function must be specified” 的错误,原因是 User 的 url 需要通过 UserList.url 或 User.urlRoot 来生成,而这里的 user 在 save() 之前并没有被加到 UserList 中去, User.urlRoot 也并没有被赋值,因此得不到 url,无法发出 POST 请求。

注释四:

经过注释三的分析,我们知道只要在 user.save() 之前对 user.urlRoot 赋值就可以了,当然也可以直接在定义 User 模型的时候给他赋值,效果一样。

注意这里先使用了一个临时变量 ul 来存放 this.userList,然后再 success 回调函数中使用的是 ul 而不是 this.userList。这样做并不是多此一举,因为在执行 success 回调函数的时候,this 所代表的对象已经不是 UserListView 了,而是 Window 对象。关于 JavaScript 里面的 this 貌似比较复杂,有兴趣的可以自己上网搜一下,我现在也只是一知半解。

posted on 2012-08-20 19:36  南京大乱炖  阅读(2301)  评论(0编辑  收藏  举报

导航