从内部指令检索输入验证错误

时间:2016-07-15 20:16:09

标签: angularjs

我已经创建了一些指令,可以帮助我检查看起来或多或少像这样的表单中的输入有效性:

app.directive("initialDate", function(){
    return{
        require: '?ngModel',
        restrict:"A",
        link:function(scope, element, attrs, ngModel){
            scope.$watch(attrs.ngModel, function() {
                validate();
            });
            attrs.$observe('initialDate', function () {
                validate();
            });
            var validate = function() {
                var date = Date.parse(ngModel.$viewValue);
                var initialDate = Date.parse(attrs.initialDate);
                ngModel.$setValidity('initial-date', date >= initialDate);
            }
        }
    }
});

现在,我想将自定义消息添加到输入,选择等等但未通过验证但是我不想一个接一个地添加一个span或somethinng与ng-if(thata)我想避免的很多工作。)

我的第一次尝试是创建一个这样的指令:

app.directive("ngInvalid", function(){
    restrict:"C",
    ...
});

但那不起作用所以现在我正在尝试这个

app.directive("input", function(){
    return {
        require:"?ngModel",
        link:function(scope, element, attrs, ctrl){
            scope.$watch(function() {
                return element.attr('class');
            }, function(newVal, oldVal){
                if ( newVal == oldVal ) return;     
                if (element.hasClass("ng-invalid")){
                    element.parent().append("<span class = 'error' style='color:red'>There was an error</span>");
                }else{
                    element.parent().find(".error").remove();
                }
            });
        }
    }
});

所以,这个工作(或多或少,它需要更多的工作,呵呵),但我必须创建一个用于选择,textareas等..并且如果我想要显示,对每种类型的错误进行一些检查自定义消息。

此外,它对我来说真的很脏。我真的认为在指令中我必须要做的事情看起来更像是这样:

...
 link:function(scope, element, attrs, ctrl){ /***element is the input or select or textarea... ***/
    if ( !element.$isValid ){
        switch ( element.$validationErrors[0] ){
            case "emailValidation": element.parent().append("<span class = 'error' style='color:red'>Email format not correct</span>"); break;
            case "initialDate": element.parent().append("<span class = 'error' style='color:red'>Date cannot be previous to {{initialDate}}</span>"); break;
            case "pattern": element.parent().append("<span class = 'error' style='color:red'>Format incorrect</span>"); break;
        }

    }
 }
...

我一直在查看文档并浏览一些问题,但大多数情况下我找到了需要使用表单控制器的解决方案,我想避免这种情况。

是否有一些方法可以让我从输入中检索错误?

修改

为了说清楚,让我们说输入不是包装在表单标签内,而是包含div或其他东西。我如何访问输入指令中的每个错误?

2 个答案:

答案 0 :(得分:1)

我发布的上一个方法存在一些问题。对类的监视没有按预期工作,当类实际更改时,它没有被触发,但是当输入下一个输入字符时。例如,当在空白之后,您在输入中引入了一个新字符,并且控制台中的日志和检查器中的类是不同的(可能是一个有角度的错误?)时,出现了所需的错误。

无论如何,我发现了我认为更好的解决方案:哦,当控件没有包含在表单标签内时,实际上可以访问有效性检查

那是代码(我还没有添加模糊检查,但应该很容易)

var errMsgDirective = function(){
    return{
        restrict:"E",
        require:"?ngModel",
        link:function(scope, element, attrs, ctrl, ngModel){
            if ( !attrs.ngModel ) return;

            var lang = navigator.language.substr(0,2) || navigator.userLanguage.substr(0,2); 
            const E_UNKNOWN = 0, E_REQUIRED = 1, E_NAN = 2, E_MIN = 3, E_MAX = 4, E_EMAIL = 5, E_MINLEN = 6, E_MAXLEN = 7, E_PATTERN = 8, E_BLACKLIST = 9, E_EQUAL = 10; E_INITIALDATE = 11;    
            const ERR_MESSAGES = [
                {'es':'Error desconocido', 'en':'Unknown Error'}, 
                {'es':'Este campo es obligatorio', 'en':'This field is mandatory'}, 
                {'es':'Este campo debe ser numérico', 'en':'This field should be numeric'}, 
                {'es':'El valor es inferior al mínimo', 'en':'Value is lower than the minimum'}, 
                {'es':'El valor es superior al máximo', 'en':'Value is higher than the maximum'}, 
                {'es':'El formato de email no es válido', 'en':'Email format incorrect'},
                {'es':'No cumple longitud mínima', 'en':'Minimum length not matched'}, 
                {'es':'No cumple longitud máxima', 'en':'Maximum length not matched'}, 
                {'es':'El formato no es válido', 'en':'Format incorrect'}, 
                {'es':'Este valor no está permitido', 'en':'This value is not allowed'}, 
                {'es':'Los campos no coinciden', 'en':'Fields doesn´t match'},
                {'es':'La fecha es anterior a la fecha inicial', 'en':'This date is previous to the initial date'},
            ];
            var checkValidity  = function(){
                if (!ctrl.$touched) return;


                var errors = [];
                for ( var i in ctrl.$error ){
                    if ( ctrl.$error[i] == false ) continue;        
                    switch (i){
                        case "required": errors.push(ERR_MESSAGES[E_REQUIRED][lang]); break;
                        case "number": errors.push(ERR_MESSAGES[E_NAN][lang]); break; 
                        case "min": errors.push(ERR_MESSAGES[E_MIN][lang]); break;
                        case "max": errors.push(ERR_MESSAGES[E_MAX][lang]); break;
                        case "email": errors.push(ERR_MESSAGES[E_EMAIL][lang]); break;
                        case "minlength": errors.push(ERR_MESSAGES[E_MINLEN][lang]); break;
                        case "maxlength": errors.push(ERR_MESSAGES[E_MAXLEN][lang]); break;
                        case "pattern": errors.push(ERR_MESSAGES[E_PATTERN][lang]); break;
                        case "blacklist": errors.push(ERR_MESSAGES[E_BLACKLIST][lang]); break;
                        case "equals": errors.push(ERR_MESSAGES[E_EQUAL][lang]); break;
                        case "initial-date": errors.push(ERR_MESSAGES[E_INITIALDATE][lang]); break;
                        default: errors.push(ERR_MESSAGES[E_UNKNOWN][lang]); break;
                    }
                }

                element.parent().find(".error").remove();
                if ( errors.length == 0){

                }else{
                    $errMessage = errors.join("; ");
                    element.parent().append("<span class = 'error'>"+$errMessage+"</span>");
                }
            }

            scope.$watch(function(){
                return JSON.stringify(ctrl.$error);
            }, function(){
                checkValidity();
            });

            scope.$watch(function(){
                return ctrl.$touched;
            }, function(){
                checkValidity();
            })
        }
    }
}

app.directive("input", errMsgDirective);
app.directive("textarea", errMsgDirective);
app.directive("select", errMsgDirective);

我想指出一些事情:

第一:我可以使用ctrl来访问指令内每个控件的所有错误。$ error(为了上帝的缘故,为什么在所有堆栈溢出中没有单一的答案说你可以使用。$ error访问控制错误??¬¬)

第二:我真的很难把手表放在手边:我尝试了以下所有方法:

scope.$watch(attrs.ngModel, function(){...}) //This one did not work because the model doesn´t change when the validity checks are not met
scope.$watch(ctrl.$valid, function(){...}) //Never fired
scope.$watch(ctrl.$invalid, function(){...}) //Never fired
scope.$watch(ctrl.$error, function(){...}) //Never fired
scope.$watch(function(){ return ctrl.$error }, function(){...}) //Never fired
scope.$watch(function(){ return JSON.stringify(ctrl.$error) }, function(){...}) //BINGO!!!

在这里你可以看到它有效:

http://codepen.io/sergio0983/pen/ZOvPvW?editors=1011

答案 1 :(得分:0)

简答

不,没有表格控制器就无法进行有效性检查。

解决方法

我使用输入指令来实现我想要的,但它对我来说似乎不太优雅(我真的想使用影响textareas的指令并选择而不必将my-custom-directive添加到每个字段我想验证)

(function(){
    var lang = navigator.language.substr(0,2) || navigator.userLanguage.substr(0,2); 

    const E_UNKNOWN = 0,
    E_REQUIRED = 1,
    E_NAN = 2,
    E_MIN = 3,
    E_MAX = 4,
    E_EMAIL = 5,
    E_MINLEN = 6,
    E_MAXLEN = 7,
    E_PATTERN = 8,
    E_BLACKLIST = 9,
    E_EQUAL = 10;
    E_INITIALDATE = 11;

    const ERR_MESSAGES = [
        {'es':'Error desconocido', 'en':'Unknown Error'}, 
        {'es':'Este campo es obligatorio', 'en':'This field is mandatory'}, 
        {'es':'Este campo debe ser numérico', 'en':'This field should be numeric'}, 
        {'es':'El valor es inferior al mínimo', 'en':'Value is lower than the minimum'}, 
        {'es':'El valor es superior al máximo', 'en':'Value is higher than the maximum'}, 
        {'es':'El formato de email no es válido', 'en':'Email format incorrect'},
        {'es':'No cumple longitud mínima', 'en':'Minimum length not matched'}, 
        {'es':'No cumple longitud máxima', 'en':'Maximum length not matched'}, 
        {'es':'El formato no es válido', 'en':'Format incorrect'}, 
        {'es':'Este valor no está permitido', 'en':'This value is not allowed'}, 
        {'es':'Los campos no coinciden', 'en':'Fields doesn´t match'},
        {'es':'La fecha es anterior a la fecha inicial', 'en':'This date is previous to the initial date'},
    ];

    var lang = navigator.language.substr(0,2) || navigator.userLanguage.substr(0,2); 

    app.directive("input", function(){
        return {
            scope:{},
            link:function(scope, element, attrs, ctrl, ngModel){
                scope.blurred = false;
                element[0].addEventListener("blur", function(){
                    scope.blurred = true;
                    element.parent().find(".error").remove();
                    checkValidity();
                });

                var checkValidity  = function(){
                    var classList = element.attr('class');
                    if (!classList) return;
                    var matches = classList.match(/ng-invalid-(.*?)(\s|$)/);
                    if ( matches == null ) return;
                    if ( !(1 in matches) ) return;

                    var $err = matches[1];
                    var $errMessage = "";

                    switch($err){
                        case "required": $errMessage = ERR_MESSAGES[E_REQUIRED][lang]; break;
                        case "number": $errMessage = ERR_MESSAGES[E_NAN][lang]; break; 
                        case "min": $errMessage = ERR_MESSAGES[E_MIN][lang]; break;
                        case "max": $errMessage = ERR_MESSAGES[E_MAX][lang]; break;
                        case "email": $errMessage = ERR_MESSAGES[E_EMAIL][lang]; break;
                        case "minlength": $errMessage = ERR_MESSAGES[E_MINLEN][lang]; break;
                        case "maxlength": $errMessage = ERR_MESSAGES[E_MAXLEN][lang]; break;
                        case "pattern": $errMessage = ERR_MESSAGES[E_PATTERN][lang]; break;
                        case "blacklist": $errMessage = ERR_MESSAGES[E_BLACKLIST][lang]; break;
                        case "equals": $errMessage = ERR_MESSAGES[E_EQUAL][lang]; break;
                        case "initial-date": $errMessage = ERR_MESSAGES[E_INITIALDATE][lang]; break;
                        default: $errMessage = ERR_MESSAGES[E_UNKNOWN][lang]; break;
                    }

                    if (element.hasClass("ng-invalid")){
                        element.parent().append("<span class = 'error'>"+$errMessage+"</span>");
                    }
                }
            }
        }
    });
})();

以下是工作码本的链接:http://codepen.io/sergio0983/pen/AXxNpN?editors=1111

出于某种原因,在codepen中,如果我没有在angular元素上使用$ selector,那么错误就不会被删除