问题
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