• 基于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 });

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

     

  • 相关阅读:
    使用go-fuse开发一个fuse 文件系统
    awesome-fuse-fs
    jdk 容器运行环境指定时区
    几个不错的golang工具包
    golang 一些不错的log 包
    mysql_fdw 集成go-mysql-server 开发的mysql server
    一些不错的golang web 框架
    golang gomail+fasttemplate+mailhog 发送邮件
    golang go-simple-mail+fasttemplate+mailhog 发送邮件
    实现一个简单的golang db driver
  • 原文地址:https://www.cnblogs.com/hiddenfox/p/2649621.html
Copyright © 2020-2023  润新知