用户离开字段后验证字段


92

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


更新:

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


3
我想这是angularjs应该支持的东西……
csg 2014年

也许。内置的表单验证效果很好,我不知道是否要内置它。毕竟,在大多数用例中,我希望Angular立即进行验证。即在数字字段中,如果用户开始输入字母,我希望表单立即失效。
mcranston18 2014年

11
自15年以来,对模糊的验证
就很

Answers:


75

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指令覆盖此行为,使其仅绑定到指定的事件列表。即ng-model-options =“ {updateOn:'blur'}”仅在控件失去焦点后才会更新和验证。您可以使用空格分隔的列表设置多个事件。即ng-model-options =“ {updateOn:'mousedown blur'}”

并反跳

您可以通过将去抖动键与ngModelOptions指令一起使用来延迟模型的更新/验证。此延迟还将适用于解析器,验证器和模型标志,例如$ dirty或$ pristine。

即ng-model-options =“ {debounce:500}”自上次内容更改以来将等待半秒钟,然后触发模型更新和表单验证。


2
在大多数情况下,这不能解决问题,因为您可能仍希望ng-change在用户离开该字段之前接收事件。例如,在我的情况下,只要输入字段有效,我就立即从API请求新数据,但我不想在无效时立即显示错误消息-我只想在输入错误时执行此操作无效,并且发生了模糊事件。
2014年

@Tom我正在测试1.3.0 rc-3,它change直到启动之前都不会启动blur,请检查以下内容:plnkr.co/edit/RivVU3r7xfHO1hUybqMu?p=preview
Gill Bates

这确实应该是公认的最佳答案。当可以添加单个属性时,为什么还要添加指令?
Dan Hulton

92

从1.3.0版开始,可以使用轻松完成此操作$touched,这在用户离开该字段之后才成立。

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

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


1
每次键入按键后,还不执行验证吗?OP表示他只想在用户离开该字段后进行验证。
comecme

@comecme是的,默认的ng-model-options updateOn: 'default'意味着对于输入,键入和更改进行选择
pocesar 2015年

5
但是,这里的最大缺陷是,如果您回到现场,则不会获得相同的验证经验。您又回到了第一个方框,因为它已经被设置为触摸过,所以它在每次击键时都会生效。我很惊讶这获得了如此多的赞誉。
dudewad

3
@dudewad是正确的,但是我认为这是UX的理想行为-如果返回无效字段,则错误消息将一直持续到该值有效为止,直到您键入第一个字母为止。
danbars

@dudewad在第一次填写字段后就已经知道该字段无效之后,是否可以帮助用户在每次印刷中进行验证?一旦他们知道该字段无效,就很可能希望快速知道该字段何时有效,并且每次按键验证都会加快该过程吗?
艾伦·邓宁

32

我通过扩展@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可用于为用户提供更好的体验。


2
为什么需要'?ngModel'而不是简单的'ngModel'?我知道需要ngModel在那里,但是为什么要问号呢?
Noremac 2013年

2
谢谢你的主意。应该指出,它需要jQuery(我认为-在jql中看不到.closest())。
iandotkelly 2013年

1
ngModel并不支持所有的<input>元素(例如,input type = submit)。?仅在支持它的元素上需要ngModel。
chmanie 2013年

5
老实说,您不应偏离设置输入字段状态的角度方法formName.inputName.$dirty。我建议编辑指令以编写类似的布尔值like formName.inputName.$focused,而不是使用类名进行模糊处理,并使用布尔值进行验证。
2014年

17

我设法用相当简单的CSS做到了这一点。这确实要求错误消息是它们所涉及的输入的同级,并且它们的类别为error

:focus ~ .error {
    display:none;
}

满足这两个要求之后,这将隐藏与聚焦输入字段有关的任何错误消息,我认为angularjs应该这样做。似乎是疏忽大意。


2
啊 这是明智之举。与编写JavaScript相比,我更喜欢这样做。
甲板

5
这种方法的缺点是,当有人纠正错误时,该错误不再可见,这是不理想的。理想的情况是该错误直到模糊才显示出来,但是直到被纠正后才消失。
克里斯·尼古拉

4

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

NG-不变NG-触摸之前分别被设置并且用户之后已聚焦的验证元件上。

的CSS

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

4

关于@lambinator的解决方案...我在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);

2

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

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

如果您不想走这条路,那么您的另一选择是在字段上设置$ watch,并在填写该字段时再次触发验证。


2

为了进一步了解给定的答案,可以使用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>

2

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

这样做很好,但是当用户在该字段中单击时,错误消息将消失,这是不理想的。
Bennett McElwee 2014年

2

这是一个使用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']);

1

AngularJS 1.3(撰写本文时为beta版)中的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>

1

使用字段状态$ touched对此字段已进行了触摸,如下例所示。

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

0

您可以使用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;
};

0

如果您使用bootstrap 3和lesscss,则可以使用以下较少的代码段启用模糊验证:

:focus ~ .form-control-feedback.glyphicon-ok {
  display:none;
}

:focus ~ .form-control-feedback.glyphicon-remove {
  display:none;
}

.has-feedback > :focus {
  & {
    .form-control-focus();
  }
}

0

我使用了指令。这是代码:

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和已访问过的css类,您会在指令中看到该类。

注意:请记住完全以这种方式将'on-blur-val'添加到您的输入控件中,且不带撇号


0

通过使用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>


0

我们可以使用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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.