【问题标题】:Validate fields after user has left a field用户离开字段后验证字段
【发布时间】:2013-03-25 18:47:17
【问题描述】:

使用 AngularJS,我可以使用 ng-pristineng-dirty 来检测用户是否输入了该字段。但是,我只想在用户离开字段区域后进行客户端验证。这是因为当用户输入诸如电子邮件或电话之类的字段时,他们总是会收到一个错误,直到他们完成完整的电子邮件输入,这并不是最佳的用户体验。

Example


更新:

Angular 现在附带一个自定义模糊事件: https://docs.angularjs.org/api/ng/directive/ngBlur

【问题讨论】:

  • 我猜这是angularjs应该支持的东西...
  • 也许吧。内置的表单验证工作得很好,我不知道我是否想要这个内置的。毕竟,大多数用例我希望 Angular 立即验证。即在数字字段中,如果用户开始输入字母,我希望表单立即失效。
  • 模糊验证自 15 年来很常见......

标签: forms angularjs


【解决方案1】:

从 1.3.0 版开始,这可以通过 $touched 轻松完成,在用户离开该字段后也是如此。

<p ng-show="form.field.$touched && form.field.$invalid">Error</p>

https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

【讨论】:

  • 不会在每次按键后仍然执行验证吗? OP 表示他只想在用户离开该字段后进行验证。
  • @comecme 是的,默认的 ng-model-options 是 updateOn: 'default',这意味着,对于输入,按键和选择,在更改时
  • 不过,这里最大的缺陷是,如果您回到现场,您将无法获得相同的验证体验。你回到了第一格,它在每次击键时都会验证,因为它已经设置为 touch。我很惊讶这有这么多的赞成票。
  • @dudewad 是正确的,但我认为这是 UX 明智的预期行为 - 如果您返回无效字段,则错误消息应该一直持续到值有效,而不是直到您输入第一个信。
  • @dudewad 在他们已经知道一个字段在他们第一次填写之后是无效的之后,它不会帮助用户在每次按下时进行验证吗?一旦他们知道该字段无效,他们很可能希望快速知道该字段何时有效并且在每次按键时进行验证可能会加快该过程?
【解决方案2】:

Angular 1.3 现在有 ng-model-options,例如,您可以将选项设置为 { 'updateOn': 'blur'},甚至可以在使用时去抖动,或者输入速度太快,或者您想节省一些昂贵的 DOM操作(例如模型写入多个 DOM 位置,并且您不希望每次按键都发生 $digest 循环)

https://docs.angularjs.org/guide/forms#custom-triggershttps://docs.angularjs.org/guide/forms#non-immediate-debounced-model-updates

默认情况下,对内容的任何更改都会触发模型更新和 表单验证。您可以使用 ngModelOptions 指令仅绑定到指定的事件列表。 IE。 ng-model-options="{ updateOn: 'blur' }" 将更新和验证 只有在控件失去焦点之后。您可以使用 空格分隔的列表。 IE。 ng-model-options="{ updateOn: 'mousedown 模糊'}"

去抖

您可以使用 debounce 键延迟模型更新/验证 使用 ngModelOptions 指令。此延迟也适用于 解析器、验证器和模型标志,如 $dirty 或 $pristine。

即ng-model-options="{ debounce: 500 }" 将等待半秒 自触发模型更新之前的最后一次内容更改和 表单验证。

【讨论】:

  • 在大多数情况下,这并不能解决问题,因为您可能仍希望在用户离开该字段之前接收ng-change 事件。例如,在我的情况下,只要输入字段有效,我就会从我的 API 请求新数据,但我不想在它无效时立即显示错误消息——我只想在它是无效并且发生了模糊事件。
  • @Tom 我正在测试1.3.0 rc-3,直到blur 才会触发change,请检查:plnkr.co/edit/RivVU3r7xfHO1hUybqMu?p=preview
  • 这应该是公认的最佳答案。既然可以添加单个属性,为什么还要添加指令?
【解决方案3】:

我通过扩展 @jlmcdonald 的建议解决了这个问题。我创建了一个指令,该指令将自动应用于所有输入和选择元素:

var blurFocusDirective = function () {
    return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, elm, attr, ctrl) {
            if (!ctrl) {
                return;
            }

            elm.on('focus', function () {
                elm.addClass('has-focus');

                scope.$apply(function () {
                    ctrl.hasFocus = true;
                });
            });

            elm.on('blur', function () {
                elm.removeClass('has-focus');
                elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

            elm.closest('form').on('submit', function () {
                elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

        }
    };
};

app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);

这将在用户关注/访问元素时将has-focushas-visited 类添加到各种元素。然后,您可以将这些类添加到您的 CSS 规则中以显示验证错误:

input.has-visited.ng-invalid:not(.has-focus) {
    background-color: #ffeeee;   
}

这很有效,因为元素仍然会获得 $invalid 属性等,但 CSS 可用于为用户提供更好的体验。

【讨论】:

  • 为什么需要'?ngModel' 而不是简单的'ngModel'?我知道要求 ngModel 存在,但为什么要问号?
  • 感谢您的想法。应该指出它需要 jQuery(我认为 - 在 jql 中看不到 .closest())。
  • ngModel 并不支持所有的 元素(例如 input type=submit)。这 ?仅在支持它的元素上需要 ngModel。
  • 老实说,您不应该偏离设置输入字段状态的角度方式,例如formName.inputName.$dirty。我建议编辑指令以编写类似的布尔值 formName.inputName.$focused,而不是使用类名进行模糊和布尔值进行验证。
【解决方案4】:

我设法用一个非常简单的 CSS 来做到这一点。这确实要求错误消息是它们相关的输入的兄弟,并且它们具有error 类。

:focus ~ .error {
    display:none;
}

满足这两个要求后,这将隐藏任何与焦点输入字段相关的错误消息,我认为 angularjs 无论如何都应该这样做。似乎是疏忽。

【讨论】:

  • 啊。这是一个聪明的方法。我更喜欢这个而不是编写 javascript 来做到这一点。
  • 这种方法的缺点是当有人纠正错误时,错误不再可见,这是不理想的。理想的情况是错误在模糊之前不会显示,但在纠正之前不会消失。
【解决方案5】:

这似乎在较新版本的 angular 中作为标准实现。

ng-untouchedng-touched 类分别在用户关注经过验证的元素之前和之后设置。

CSS

input.ng-touched.ng-invalid {
   border-color: red;
}

【讨论】:

    【解决方案6】:

    关于@lambinator's 解决方案...我在 angular.js 1.2.4 中收到以下错误:

    错误:[$rootScope:inprog] $digest 已经在进行中

    我不确定我是否做错了什么,或者这是否是 Angular 的变化,但删除 scope.$apply 语句解决了问题,并且类/状态仍在更新。

    如果您也看到此错误,请尝试以下操作:

    var blurFocusDirective = function () {
      return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, elm, attr, ctrl) {
          if (!ctrl) {
            return;
          }
          elm.on('focus', function () {
            elm.addClass('has-focus');
            ctrl.$hasFocus = true;
          });
    
          elm.on('blur', function () {
            elm.removeClass('has-focus');
            elm.addClass('has-visited');
            ctrl.$hasFocus = false;
            ctrl.$hasVisited = true;
          });
    
          elm.closest('form').on('submit', function () {
            elm.addClass('has-visited');
    
            scope.$apply(function () {
              ctrl.hasFocus = false;
              ctrl.hasVisited = true;
            });
          });
        }
      };
    };
    app.directive('input', blurFocusDirective);
    app.directive('select', blurFocusDirective);
    

    【讨论】:

      【解决方案7】:

      编写一个包装 javascript blur() 方法(并在触发时运行验证函数)的自定义指令可能对您有用;有一个 Angular 问题有一个示例问题(以及一个可以绑定到 Angular 本身不支持的其他事件的通用指令):

      https://github.com/angular/angular.js/issues/1277

      如果你不想走那条路,你的另一个选择是在字段上设置 $watch,当字段被填写时再次触发验证。

      【讨论】:

        【解决方案8】:

        为了进一步了解给定的答案,您可以通过使用 CSS3 伪类来简化输入标记,并且只使用类标记访问过的字段以延迟显示验证错误,直到用户失去对字段的关注:

        (示例需要 jQuery)

        JavaScript

        module = angular.module('app.directives', []);
        module.directive('lateValidateForm', function () {
            return {
                restrict: 'AC',
                link: function (scope, element, attrs) {
                    $inputs = element.find('input, select, textarea');
        
                    $inputs.on('blur', function () {
                        $(this).addClass('has-visited');
                    });
        
                    element.on('submit', function () {
                        $inputs.addClass('has-visited');
                    });
                }
            };
        });
        

        CSS

        input.has-visited:not(:focus):required:invalid,
        textarea.has-visited:not(:focus):required:invalid,
        select.has-visited:not(:focus):required:invalid {
          color: #b94a48;
          border-color: #ee5f5b;
        }
        

        HTML

        <form late-validate-form name="userForm">
          <input type="email" name="email" required />
        </form>
        

        【讨论】:

          【解决方案9】:

          基于@nicolas 的回答。纯 CSS 应该是诀窍,它只会在模糊时显示错误消息

          <input type="email" id="input-email" required
                         placeholder="Email address" class="form-control" name="email"
                         ng-model="userData.email">
                  <p ng-show="form.email.$error.email" class="bg-danger">This is not a valid email.</p>
          

          CSS

          .ng-invalid:focus ~ .bg-danger {
               display:none;
          }
          

          【讨论】:

          • 这样很好,但是当用户在该字段中点击返回时错误信息会消失,这并不理想。
          【解决方案10】:

          这是一个使用 ng-messages(在 Angular 1.3 中可用)和自定义指令的示例。

          当用户第一次离开输入字段时,验证消息会显示在模糊上,但当他更正值时,验证消息会立即被删除(不再模糊)。

          JavaScript

          myApp.directive("validateOnBlur", [function() {
              var ddo = {
                  restrict: "A",
                  require: "ngModel",
                  scope: {},
                  link: function(scope, element, attrs, modelCtrl) {
                      element.on('blur', function () {
                          modelCtrl.$showValidationMessage = modelCtrl.$dirty;
                          scope.$apply();
                      });
                  }
              };
              return ddo;
          }]);
          

          HTML

          <form name="person">
              <input type="text" ng-model="item.firstName" name="firstName" 
                  ng-minlength="3" ng-maxlength="20" validate-on-blur required />
              <div ng-show="person.firstName.$showValidationMessage" ng-messages="person.firstName.$error">
                  <span ng-message="required">name is required</span>
                  <span ng-message="minlength">name is too short</span>
                  <span ng-message="maxlength">name is too long</span>
              </div>
          </form>
          

          PS。不要忘记下载 ngMessages 并将其包含在您的模块中:

          var myApp = angular.module('myApp', ['ngMessages']);
          

          【讨论】:

            【解决方案11】:

            AngularJS 1.3(撰写本文时为测试版)中的 ng-model-options 已记录为支持 {updateOn: 'blur'}。对于早期版本,以下内容对我有用:

            myApp.directive('myForm', function() {
              return {
                require: 'form',
                link: function(scope, element, attrs, formController) {
                  scope.validate = function(name) {
                    formController[name].isInvalid
                        = formController[name].$invalid;
                  };
                }
              };
            });
            

            使用这样的模板:

            <form name="myForm" novalidate="novalidate" data-my-form="">
            <input type="email" name="eMail" required="required" ng-blur="validate('eMail')" />
            <span ng-show="myForm.eMail.isInvalid">Please enter a valid e-mail address.</span>
            <button type="submit">Submit Form</button>
            </form>
            

            【讨论】:

              【解决方案12】:

              使用字段状态 $touched 字段已为此被触摸,如下例所示。

              <div ng-show="formName.firstName.$touched && formName.firstName.$error.required">
                  You must enter a value
              </div>
              

              【讨论】:

                【解决方案13】:

                您可以使用 ng-class 和关联控制器范围内的属性动态设置 has-error css 类(假设您使用的是引导程序):

                plunkr:http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info

                HTML:

                <div ng-class="{'has-error': badEmailAddress}">
                    <input type="email" class="form-control" id="email" name="email"
                        ng-model="email" 
                        ng-blur="emailBlurred(email.$valid)">
                </div>
                

                控制器:

                $scope.badEmailAddress = false;
                
                $scope.emailBlurred = function (isValid) {
                    $scope.badEmailAddress = !isValid;
                };
                

                【讨论】:

                  【解决方案14】:

                  如果您使用 bootstrap 3 和 lesscss,您可以使用以下 less sn-p 启用模糊验证:

                  :focus ~ .form-control-feedback.glyphicon-ok {
                    display:none;
                  }
                  
                  :focus ~ .form-control-feedback.glyphicon-remove {
                    display:none;
                  }
                  
                  .has-feedback > :focus {
                    & {
                      .form-control-focus();
                    }
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    out我使用了一个指令。代码如下:

                    app.directive('onBlurVal', function () {
                        return {
                            restrict: 'A',
                            link: function (scope, element, attrs, controller) {
                    
                                element.on('focus', function () {
                                    element.next().removeClass('has-visited');
                                    element.next().addClass('has-focus');
                                });
                    
                                element.on('blur', function () {
                    
                                    element.next().removeClass('has-focus');
                                    element.next().addClass('has-visited');
                                });
                            }
                        }
                    })
                    

                    我所有的输入控件都有一个 span 元素作为下一个元素,这是我的验证消息显示的地方,因此该指令作为属性添加到每个输入控件。

                    我的 css 文件中还有(可选).has-focus 和 has-visited css 类,您可以看到指令中引用了这些类。

                    注意:请记住以这种方式将“on-blur-val”添加到您的输入控件中,不要使用撇号

                    【讨论】:

                      【解决方案16】:

                      通过使用 ng-focus,您可以实现目标。您需要在输入字段中提供 ng-focus 。在编写您的 ng-show 衍生品时,您还必须编写一个不相等的逻辑。如下代码:

                      <input type="text" class="form-control" name="inputPhone" ng-model="demo.phoneNumber" required ng-focus> <div ng-show="demoForm.inputPhone.$dirty && demoForm.inputPhone.$invalid && !demoForm.inputPhone.$focused"></div>

                      【讨论】:

                        【解决方案17】:

                        我们可以使用 onfocus 和 onblur 函数。将是简单和最好的。

                        <body ng-app="formExample">
                          <div ng-controller="ExampleController">
                          <form novalidate class="css-form">
                            Name: <input type="text" ng-model="user.name" ng-focus="onFocusName='focusOn'" ng-blur="onFocusName=''" ng-class="onFocusName" required /><br />
                            E-mail: <input type="email" ng-model="user.email" ng-focus="onFocusEmail='focusOn'" ng-blur="onFocusEmail=''" ng-class="onFocusEmail" required /><br />
                          </form>
                        </div>
                        
                        <style type="text/css">
                         .css-form input.ng-invalid.ng-touched {
                            border: 1px solid #FF0000;
                            background:#FF0000;
                           }
                         .css-form input.focusOn.ng-invalid {
                            border: 1px solid #000000;
                            background:#FFFFFF;
                         }
                        </style>
                        

                        在这里试试:

                        http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2019-02-08
                          • 1970-01-01
                          • 2011-08-29
                          • 2017-04-07
                          • 1970-01-01
                          • 2016-03-05
                          相关资源
                          最近更新 更多