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

3.6 添加validate

使用 JavaScript 实现表单的验证是一个很常见的问题,jQuery 有不少表单验证的插件,只需要经过简单的配置就能完成验证的功能。

因为是初学 JavaScript,所以先自己用 jQuery 写了一个表单验证的例子,然后讨论一下怎样将 jQuery 的表单验证用到 Backbone.js 构建的程序中,最后使用 Backbone.Model 的 validate() 方法来实现验证的过程。

3.6.1 使用 jQuery 实现的表单验证

表单只有四个 input,界面如图所示:

各字段的验证规则如下:

6.1.html 表单相关的 HTML 代码如下,因为想熟悉一下 jQuery 选择器的使用,所以 HTML 中没有使用任何的 id/class:

 1 <form action="#" method="post">
 2     <p>
 3         <label for="username">Username:</label>
 4         <input type="text" name="username" />
 5     </p>
 6     <p>
 7         <label for="password">Password:</label>
 8         <input type="text" name="password" />
 9     </p>
10     <p>
11         <label for="re-password">Re-password:</label>
12         <input type="text" name="re-password" />
13     </p>
14     <p>
15         <label for="email">Email:</label>
16         <input type="text" name="email" />
17     </p>
18     <p><input type="submit" value="Register" /></p>
19 </form>

jquery-validate-1.js 的源码如下,关键的地方已经给出注释:

 1 $(document).ready(function(){
 2 
 3     //光标移动到username的input时触发,如果已经有错误/正确提示则不改变提示,如果没有提示输入
 4     $("input[name='username']").focus(function(){
 5         if ($("input[name='username']+span.error").size() == 0 
 6             && $("input[name='username']+span.success").size() == 0) {
 7             $(this).after("<span class='hint'>Please enter your username.</span>");
 8         }
 9     });
10     //光标移出username的input时触发
11     $("input[name='username']").blur(function(){
12         $("input[name='username']+span").remove(); //去掉之前的提示
13         if($(this).val().length < 6 || $(this).val().length>12){ //判断长度,给出提示
14             $(this).after("<span class='error'>Username contains 6-12 characters.</span>");
15         }else{ //向服务器发出AJAX请求,判断username是否已经存在,给出提示
16             $.ajax({
17                 url:"./rest/user/validate/"+$(this).val(),
18                 success:function(data, textStatus){
19                     if(data == "true"){
20                         $("input[name='username']").after("<span class='success'>Passed.</span>");
21                     }else{
22                         $("input[name='username']").after("<span class='error'>Username has been used.</span>");
23                     }
24                 }
25             });
26         }
27     });
28 
29     $("input[name='password']").focus(function(){
30         if ($("input[name='password']+span.error").size() == 0 
31             && $("input[name='password']+span.success").size() == 0) {
32             $(this).after("<span class='hint'>Please enter password again.</span>");
33         }
34     });
35     $("input[name='password']").blur(function(){
36         $("input[name='password']+span").remove();
37         if($(this).val().length < 6 || $(this).val().length>12){
38             $(this).after("<span class='error'>Password contains 6-12 characters.</span>");
39         }else{
40             $(this).after("<span class='success'>Passed.</span>");
41         }
42     });
43 
44 
45     $("input[name='re-password']").focus(function(){
46         if ($("input[name='re-password']+span.error").size() == 0 
47             && $("input[name='re-password']+span.success").size() == 0 ) {
48             $(this).after("<span class='hint'>Please enter your password again.</span>");
49         }
50     });
51     $("input[name='re-password']").blur(function(){
52         $("input[name='re-password']+span").remove();
53         if($(this).val() ==""){
54             $(this).after("<span class='error'>Please enter your password again.</span>");
55         }else if($(this).val() != $("input[name='password']").val()){ //判断两次输入的password是否相同
56             $(this).after("<span class='error'>Two passwords are not same.</span>");
57         }else{
58             $(this).after("<span class='success'>Passed.</span>");
59         }
60     });
61 
62     $("input[name='email']").focus(function(){
63         if ($("input[name='email']+span.error").size() == 0 
64             && $("input[name='email']+span.success").size() == 0 ) {
65             $(this).after("<span class='hint'>Please enter your email.</span>");
66         }
67     });
68     $("input[name='email']").blur(function(){
69         $("input[name='email']+span").remove();
70         if(!$(this).val().match(/^\w{3,}@\w+(\.\w+)+$/)){ //正则表达式判断email格式是否正确
71             $(this).after("<span class='error'>Invalid email format.</span>");
72         }else{
73             $(this).after("<span class='success'>Passed.</span>");
74         }
75     });
76 
77     //表单提交的时候触发所有input的blur事件,如果没有error才能提交表单
78     $("form").submit(function(){
79         $("input").blur();
80         if ($("form span.error").size() > 0) {
81             return false;
82         }
83     });
84 
85 });

注意看78-83行,表单提交的事件(submit)发生时手动触发了所有 input 的 blur 事件,如果没有 error 才能提交表单。这样可以避免没有填写某个 input,而导致它的 blur 事件没有触发,并没有进行正确的验证。

写这个例子时也遇到了一些问题,在解决问题的过程中学到了不少,对 jQuery 的选择器、AJAX请求、页面操作等等都更熟悉了。

3.6.2 将 jQuery 的表单验证用到 Backbone.js 的程序中

上面 jQuery 的表单验证是在我学习 Backbone.js 之前写的,因此和我们的这个示例程序不太一样,我们之前写的 Backbone.js 程序中没有表单,少了 re-password,多了 phone,也懒得再改了,直接拿过来用吧,反正关键问题是如何整合起来使用。

我们简单地将 jquery-validate-1.js 引入到 5.html 文件中,测试发现根本不起作用。原因是 jquery-validate-1.js 中使用的是最常见的绑定事件的方法,只能将事件绑定到 $(document).ready() 执行的时候已经存在的页面元素上,而我们的所有 input 都是后来由 Backbone.js 显示出来的,所以绑定不上。

jQuery 中对事件的绑定有 bind() live() delegate() 三种方法,三者的区别看这里:http://kb.cnblogs.com/page/94469/。简单地来说 bind() 将事件绑定到特定的 DOM 元素上,加载的时候不存在就绑不上;live() 将事件绑定到 $(document) 元素上,事件发生的时候动态地去搜索符合要求的 DOM 元素;delegate() 将事件绑定到某个 DOM 元素上,事件发生的时候动态地去它的所有子元素里面搜索符合要求的 DOM 元素。因此我们使用 delegate() 改编一下 jquery-validate-1.js。

例如,在jquery-validate-1.js 中

$("input[name='username']").focus(function(){...});

 改为 jquery-validate-2.js 中的:

$("#right").delegate("input[name='username']","focus",function(){...});

 其他 input 相关的事件也这么修改一下就行了,测试之后发现对 input 的验证都可以正常使用了。

但是我们的程序中没有表单,因此也不会触发 submit 事件,只需要将 submit 事件上绑定的的内容移到 UserInfoView.submitEdit() UserListView.submitUserForm() 中,在 model 向服务器端 sync 之前判断有没有 error,如果有 error 就阻止 sync 的执行。mvc6.2.js 代码如下所示:

 1 var UserInfoView = Backbone.View.extend({
 2     submitEdit : function() { //修改
 3         $("input").blur();
 4         if ($("span.error").size() > 0) {
 5             return;
 6         }
 7         this.model.save({ 
 8             "username":$("input[name='username']").val(),
 9             "password":$("input[name='password']").val(),
10             "email":$("input[name='email']").val(),
11             "phone":$("input[name='phone']").val(),
12         });
13         this.$("#user-info").removeClass("editing"); 
14     },
15     ...//其余不变
16 });
17 
18 var UserListView = Backbone.View.extend({
19     submitUserForm : function() { //修改
20         $("input").blur();
21         if ($("span.error").size() > 0) {
22             return;
23         }
24         var user  = new User({
25             "username":$("input[name='username']").val(),
26             "password":$("input[name='password']").val(),
27             "email":$("input[name='email']").val(),
28             "phone":$("input[name='phone']").val(),
29         });
30 
31         this.userList.create(user,{wait:true});
32         $("input[name='username']").val(""),
33         $("input[name='password']").val(""),
34         $("input[name='email']").val(""),
35         $("input[name='phone']").val(""),
36         alert("Add a user!");
37     },
38     ...//其余不变
39 });

 还有一个方法就是将这部分内容移到 Backbone.Model 的 validate() 方法中去,也就是说还可以这么修改:

1 var User = Backbone.Model.extend({
2     validate : function(attrs) { //新增
3         $("input").blur();
4         if ($("span.error").size() > 0) {
5             return "hahaha"; //随便返回什么,触发error事件就能阻止sync的发生了
6         }
7     }
8 });

 上面两个解决方法选一个就行了。关于 validate() 方法的详解请看下一小节。

3.6.3 使用 Backbone.Model 的 validate() 实现验证

首先来看一下官网对 validate() 的说明:

model.validate(attributes) 该方法是未定义的,如果有在Javascript执行的需要,建议用自定义的验证逻辑重载它。 validate 会在 set 和 save 之前调用,并传入待更新的属性。 如果模型和属性通过验证,不返回任何值; 如果属性不合法,返回一个可选择的错误。该错误可以是简单的用于显示的字符串错误信息, 或者是一个可以描述错误详细的 error 对象。 如果 validate 返回错误,set 和 save 将不会执行。 失败的验证会触发一个 "error" 事件。

所以说 validate() 只能对 model 的所有属性整体做一次验证,然后返回错误信息,不能像 jQuery 那样在填完每一个 input 之后都进行验证。

Model 里面虽然没有定义 validate() 方法,但是有个 _validate() 方法,会在 Model.set()、Model.save()、Collection.create() 中被调用,_validate() 的源码如下: 

 1 // Run validation against the next complete set of model attributes, returning `true` if all is well. If a specific `error` callback has been passed, call that instead of firing the general `"error"` event.
 2 _validate: function(attrs, options) {
 3     if (options.silent || !this.validate) return true;
 4     attrs = _.extend({}, this.attributes, attrs);
 5     var error = this.validate(attrs, options);
 6     if (!error) return true;
 7     if (options && options.error) {
 8         options.error(this, error, options);
 9     } else {
10         this.trigger('error', this, error, options);
11     }
12     return false;
13 }

可以看出来,如果 validate() 有返回值,那么就会触发 error 事件(10行),并且有 this、error、options 三个参数,我们可以根据第二个参数来输出错误信息。

首先在 6.3.html 的表单中加入显示错误信息的部分:

 1 <!-- 增加显示错误信息的位置  -->
 2 <script type="text/template" id="user-info-template">
 3     <h3>User Information</h3>
 4     <button id="edit">Edit</button>
 5     <p class="error"></p>
 6     <ul id="user-info">
 7         <li>ID:<span><%= id %></span></li>
 8         <li>Username:<span><%= username %></span><input type="text" name="username" value="<%= username %>" /></li>
 9         <li>Password:<span><%= password %></span><input type="password" name="password" value="<%= password %>" /></li>
10         <li>Email:<span><%= email %></span><input type="text" name="email" value="<%= email %>" /></li>
11         <li>Phone:<span><%= phone %></span><input type="text" name="phone" value="<%= phone %>" /></li>
12         <button id="edit-submit">Submit</button>
13     </ul>
14 </script>
15 <!-- 增加显示错误信息的位置  -->
16 <script type="text/template" id="user-form-template">
17     <h3>Add User</h3>
18     <p class="error"></p>
19     <ul id="user-form" class="editing">
20         <li>Username:<input type="text" name="username" /></li>
21         <li>Password:<input type="password" name="password" /></li>
22         <li>Email:<input type="text" name="email" /></li>
23         <li>Phone:<input type="text" name="phone" /></li>
24         <button id="add-submit">Submit</button>
25     </ul>
26 </script>

然后对 mvc6.3.js 进行修改,在 User 中加入 validate() 方法,在 UserInfoView.submitEdit()、UserListView.submitUserForm() 中加入对错误信息的显示:

 1 $(document).ready(function() { 
 2     
 3     var User = Backbone.Model.extend({
 4         validate : function(attrs) { //新增,验证属性是否合法
 5             if (attrs.username.length<6 || attrs.username.length>12) {
 6                 return "Username contains 6-12 characters.";
 7             }
 8             var obj = $.ajax({
 9                 url : "./rest/user/validate/" + attrs.username, 
10                 async : false,
11             });
12             if (obj.responseText == "false") {
13                 return "Username has been used.";
14             }
15             if (attrs.password.length<6 || attrs.password.length>12) {
16                 return "Password contains 6-12 characters.";
17             }
18             if (attrs.email.length == 0) {
19                 return "Please enter your email.";
20             }
21             if(!attrs.email.match(/^\w{3,}@\w+(\.\w+)+$/)){
22                 return "Invalid email format.";
23             }
24         },
25     });
26     
27     
28     var UserInfoView = Backbone.View.extend({
29         submitEdit : function() { //修改
30             var m = this.model;
31             this.model.save({ 
32                 "username":$("input[name='username']").val(),
33                 "password":$("input[name='password']").val(),
34                 "email":$("input[name='email']").val(),
35                 "phone":$("input[name='phone']").val(),
36             },
37             {
38                 error : function(m,error) {  //显示错误信息
39                     $(".error").html(error);
40                 },
41                 success : function() {
42                     $("#user-info").removeClass("editing"); 
43                 }
44             });
45         },
46         ... //其余不变
47     });
48     
49     var UserListView = Backbone.View.extend({
50         submitUserForm : function() {
51             var user  = new User({
52                 "username":$("input[name='username']").val(),
53                 "password":$("input[name='password']").val(),
54                 "email":$("input[name='email']").val(),
55                 "phone":$("input[name='phone']").val(),
56             });
57             var m = this.model; //修改
58             this.userList.create(user,{
59                 wait : true,
60                 error : function(m,error) { //显示错误信息
61                     $(".error").html(error);
62                 },
63                 success : function() {
64                     $("input[name='username']").val(""),
65                     $("input[name='password']").val(""),
66                     $("input[name='email']").val(""),
67                     $("input[name='phone']").val(""),
68                     alert("Add a user!");
69                 }
70             });
71         },
72         ... //其余不变
73     });
74     ... //其余不变
75 });

完成之后的界面如下图所示:

 

原文地址:https://www.cnblogs.com/hiddenfox/p/2649621.html