Directive to add ng-pattern attribute

▼魔方 西西 提交于 2019-12-21 21:29:35

问题


I am trying to create a directive that will replace itself with the ng-pattern attribute. The attribute gets applied to the input element but the element basically becomes unusable after that. I can no longer enter characters into the text box.

Here is the plunkr http://plnkr.co/edit/F6ZQYzxd8Y04Kz8xQmnZ?p=preview

I think I must be compiling the element incorrectly after the attribute is added.

app.directive('passwordPattern', ['$compile', function($compile){
    return{
    compile: function (element, attr){

        element.removeAttr('password-pattern');
        element.attr('ng-pattern', '/^[\\w\\S]{6,12}$/');

        return {
            pre: function preLink(scope, iElement, iAttrs, controller) {  },
            post: function postLink(scope, iElement, iAttrs, controller) {
                $compile(iElement)(scope);
            }
        };
    }
};
}]);

Any thoughts on a solution or why the textbox becomes unusable would be greatly apprecitated. Thanks.


回答1:


In addition to priority: 1000, you need to add terminal: true.

The issue is that without terminal: true, the input directive gets compiled twice, and 2 sets of change listeners are getting added, which throws the ngModel directive logic off a bit.

The first compile Angular performs doesn't see the ng-pattern, so the input directive doesn't add the validateRegex parser to its list of parsers. However, the second compile (via your $compile(iElement, scope)) sees the ng-pattern and does add the validateRegex parser.

When you type, say 3, into the input box, the first change listener is called and sees the number 3. Since no ng-pattern was applied (which would've added the validateRegex $parser), no $parsers exist and the model is updated with 3 immediately.

However, when the second change listener is called, it sees the ng-pattern and calls validateRegex, which calls ngModel.$setValidity('pattern', false) and returns undefined (because the model should never be set to an invalid value). Here's the kicker - inside the ngModel directive, since the previous $viewValue of 3 and new value of undefined are out of sync, Angular calls the input directive's $render function, which updates the input to be empty. Thus when you type 3 (or anything) into the input box, it's immediately removed and appears to be broken.

A high priority (like 1000) and terminal: true will prevent the input directive (and most likely other directives unless you have one that's priority: 1001+) from being compiled the first time. This is great because you want the input directive to take into account ng-pattern - not without it in place. You don't want multiple sets of change listeners added to the same element or it may (and will) cause strange side-effects.




回答2:


Another solution will be to override the pattern property of the $validators object in ngModel controller.

You can see an example of a validator function in ngModelController docs

Here's an example in a directive:

angular.module('mymodule')
    .directive('mydirective', MyDirective);

function MyDirective() {
    return {
        restrict: 'A',
        require: 'ngModel',
        scope: {},
        link: function(scope, element, attrs, ngModelController) {
            ngModelController.$validators["pattern"] = validatePattern;

            function validatePattern(modelValue, viewValue) {
                var value = modelValue || viewValue;
                return /[0-9]+/.test(value);
            }
        }
    }
}

You can modify the above example to receive the pattern from the outside scope and change the validation function using a scope.$watch on the pattern.



来源:https://stackoverflow.com/questions/22823545/directive-to-add-ng-pattern-attribute

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!