I've just come across a strange behavior from Angular:
Here's the scenario:
In a registration form, I want to check for email uniqueness (through an http call to server).
Thus, I created a directive called emailUnique
whose client code is:
<form name="form" novalidate>
<!-- some other fields -->
<input name="email" type="email" ng-model="user.email" required email-unique/>
</form>
For the rest of the post, let's suppose that user is typing: michael
, that is clearly not a valid mail.
Let's take a look at the interesting portion of my directive code, triggering the behavior I'm interested to:
angular.module('directives.emailUnique', [])
.directive('emailUnique', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, el, attrs, ctrl) {
ctrl.$parsers.push(function (viewValue) {
console.log(viewValue); //What do you expect here for viewValue? answer below
});
}
};
});
Before giving the answer, at first glance, the response would logically be:
undefined
Why? Because:
type="email"
attribute and not simply type="text"
michael
isn't a valid mail.After testing it, the answer is undefined
as expected. My complete directive's logic will be based on that and the whole works fine.
Now, let's rename the directive: emailUnique
becoming somethingUnique
.
Client being now:
<input name="email" type="email" ng-model="user.email" required something-unique/>
Surprise: the console.log(viewValue)
is now displaying: michael
, not undefined
...
Clearly, starting with email
for the name has a strange effect when dealing with an email field in this case.
My question is simple: Is there a good reason? A possible bug? Might I misunderstand some notion?
Some further precisions:
email
attribute that could interfere with email-unique
. Indeed, it's based on the type="email"
novalidate
attribute is present or not.The issue is the directive's priority. Since you are dependent on the timing of when your parsers are added you want to set the directive's priority- that will ensure the timing you need.
In your demo the somethingUnique
directive is running before validation has been added to the parsers list (it ends up the middle of 3 parsers). Whereas with emailUnique
it's added after.
Setting the priority of your directive to something over 0 ensures it fires after emailValidation giving you undefined
always (noting from the $compile docs: "post-link functions are run in reverse order"). To confirm this you can force emailUnique
to fail by setting it's priority to something less than 0.
So this fixes the problem:
.directive('somethingUnique', function () {
return {
restrict: 'A',
require: 'ngModel',
priority: 100,
link: function (scope, el, attrs, ctrl) {
ctrl.$parsers.push(function (viewValue) {
console.log(viewValue);
});
}
};
});
Updated plunker
Update on the name issue: It appears that Angular processes directives with the same priority in alphabetical order. So homethingUnique
acts like emailUnique
since both come before input
while jomehtingUnique
behaves like somethingUnique
- running after input.
But Angular's docs say: "The order of directives with the same priority is undefined." So we can't count on alphabetical order.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With