5

I have a simple html form containing regular text input. ng-minlength, ng-maxlength and ng-pattern angular built-in form input directives are set on the input.

Problem: ng-pattern check is applied before the length check by ng-minlength and ng-maxlength.

Question: how can I change the default check order: i.e. first check for the length, then apply pattern check?

Example:

<body ng-app>

    <div>
        <form name="myForm">
            Name: <input name="name" type="text" ng-model="name" ng-minlength="3" ng-maxlength="16" ng-pattern="/^\w+$/"/>
            <div ng-show="myForm.name.$dirty && myForm.name.$invalid">
                <span ng-show="myForm.name.$error.pattern">Pattern error</span>
                <span ng-show="myForm.name.$error.minlength || myForm.name.$error.maxlength">Length error</span>
            </div>
            <br/>
            <input type="submit" value="Submit">
        </form>
    </div>

</body>

Current behavior:

  • enter "#" - see "Pattern error"
  • enter "###" - see "Pattern error"

Desired behavior:

  • enter "#" - see "Length error"
  • enter "###" - see "Pattern error"

FYI, related jsfiddle.

Thanks in advance.

4

5 回答 5

5

编写自己的指令:

var mod = angular.module("myApp", []);

mod.directive("nameValidation", function () {
    return {
        restrict: "A",
        require: "ngModel",
        link: function (scope, element, attrs, ngModelCtrl) {
            var validate = function (value) {
                var minLen = parseInt(attrs.myMinlength, 10),
                    maxLen = parseInt(attrs.myMaxlength, 10),
                    pattern = attrs.myPattern,
                    match = pattern.match(/^\/(.*)\/([gim]*)$/),
                    lenErr = false;

                if (match) {
                    pattern = new RegExp(match[1], match[2]);   
                }
                if (!ngModelCtrl.$isEmpty(value)) {
                    ngModelCtrl.$setValidity("pattern", true);
                    if ((minLen && value.length < minLen) || (maxLen && value.length > maxLen)) {
                        ngModelCtrl.$setValidity("length", false);
                        lenErr = true;
                    }
                    else {
                        ngModelCtrl.$setValidity("length", true);
                        lenErr = false;
                    }

                    if (!lenErr) {
                        if (match && !pattern.test(value)) {
                            ngModelCtrl.$setValidity("pattern", false);
                        }
                        else {
                            ngModelCtrl.$setValidity("pattern", true);
                        }
                    }
                }
                else {
                    ngModelCtrl.$setValidity("length", true);
                    ngModelCtrl.$setValidity("pattern", true);
                }
            }

            ngModelCtrl.$parsers.push(validate);
            ngModelCtrl.$formatters.push(validate);
        }
    }
});

然后在您的 HTML 中,包含应用程序并使用指令:

<body ng-app="myApp">

<div>
    <form name="myForm">
        Name: <input name="name" type="text" ng-model="name" name-validation="" my-minlength="3" my-maxlength="16" my-pattern="/^\w+$/"/>
        <div ng-show="myForm.name.$dirty && myForm.name.$invalid">
            <span ng-show="myForm.name.$error.pattern">Pattern error</span>
            <span ng-show="myForm.name.$error.length">Length error</span>
        </div>
        <br/>
        <input type="submit" value="Submit">
    </form>
</div>
</body>

该指令对三个值使用 my-minlength、my-maxlength 和 my-pattern。如果长度失败,那将首先跳闸。如果不匹配,则如果不匹配,则模式将显示为错误。如果您想在除名称之外的其他地方使用此指令,请考虑重命名此指令,因为 minlength、maxlength 和模式可以通过属性传递给它。如果它们被忽略,它们将被忽略。

见jsfiddle:http: //jsfiddle.net/4zpxk/6/

于 2014-03-19T18:22:12.390 回答
2

我在角度代码中搜索了为什么会出现这种行为。然后在函数'textInputType'中,它是处理角度'input'指令的文本输入的特定函数,我在这个函数的末尾找到了这个,我们可以看到三个代码块。

// pattern validator
if (pattern){
 //validator logic
}

// min length validator
if (attr.ngMinlength) {
 //validator logic
}

 // max length validator
if (attr.ngMaxlength) {
 //validator logic
}

因此,无论您是否在 html 输入元素中更改 ng-* 属性的声明顺序,您总是会得到相同的结果,但是如果您更改块的顺序,我的意思是,将最小长度验证器块放在模式验证器块之前你会得到你期望的结果。

这是解决您的问题的方法,但是您必须对角度代码进行一点更改,我不知道您是否真的喜欢这个。但是您遇到了一个非常常见的情况,即验证概念的声明顺序很重要,因此,必须做更多的事情来处理这个问题。谢谢

于 2014-10-08T15:50:12.530 回答
1

很遗憾,您无法更改默认支票顺序。

一种解决方案是编写自定义验证器,并不难。基于这个答案,我想出了这个代码(小提琴

  1. 用法:作用域中有一组验证函数,它们被传递给我们的自定义指令"validators",如下所示:

    <input name="name" type="text" ng-model="name" validators="nameValidators"/>
    
  2. 验证器函数看起来像(例如,对于minlength约束):

    function minlength(value, ngModel) {
        if( value == null || value == "" || value.length >= 3 ) {
            ngModel.$setValidity('minlength', true);
            return value;
        }
        else {
            ngModel.$setValidity('minlength', false);
            return;
        }
    }
    

    重要的一点是:它接受 value 和ngModelas 参数,执行测试(此处value.length >= 3)并酌情调用ngModel.$setValidity()

  3. 该指令将给定的函数注册为ngModel.$parsers

    app.directive("validators", function($parse) {
        return {
            restrict: "A",
            require: "ngModel",
            link: function(scope, el, attrs, ngModel) {
                var getter = $parse(attrs.validators),
                    validators = getter(scope),
                    i;
    
                for( i=0; i < validators.length; i++ ) {
                    ngModel.$parsers.push((function(index) {
                        return function(value) {
                            return validators[index](value, ngModel);
                        };
                    })(i));
                }
            }
        };
    });
    

许多细节可以调整和改进,但大纲有效(再次链接到fiddle)。nameValidators现在验证的顺序由数组中验证器函数的顺序显式设置。

于 2014-03-19T17:45:15.617 回答
1

如果您使用 ng-messages,您应该能够通过 ng-message 元素的顺序设置顺序,例如:

<div ng-messages="field.$error">
  <ul class="validation-errors">
    <li ng-message="required">This has the highest prio</li>
    <li ng-message="min">Second in command</li>
    <li ng-message="max">I'm last</li>
  </ul>
</div>

还有关于此的文档:https ://docs.angularjs.org/api/ngMessages/directive/ngMessages

于 2015-11-17T13:16:46.743 回答
0

我只是改变了你的指令的顺序,首先是模式

<input name="name" type="text" ng-model="name" ng-pattern="/^\w+$/" ng-minlength="3" ng-maxlength="16"/>

编辑:uuum,测试你的fiddel没有改变,它显示了你想要的行为......指令是按优先级编译的,但我不知道如何设置angulars指令优先级......对不起,应该先测试一下

于 2014-03-19T17:04:12.587 回答