Angularjs $ setValidity does not work when checking email uniqueness

I use the directive to check if an email is registered in my database.

app.directive('uniqueEmail', function($http) { var toId; return { restrict: 'A', require: 'ngModel', link: function(scope, elem, attr, ctrl) { //when the scope changes, check the email. scope.$watch(attr.ngModel, function(value) { // if there was a previous attempt, stop it. if(toId) clearTimeout(toId); // start a new attempt with a delay to keep it from // getting too "chatty". toId = setTimeout(function(){ // call to some API that returns { isValid: true } or { isValid: false } $http.get('check/e/' + value).success(function(data) { //set the validity of the field ctrl.$setValidity("uniqueEmail", data.isValid); }); }, 200); }) } } }); 

and my input:

 <form name="myForm"> <input type="email" ng-model="userEmail" name="userEmail" required unique-email/> <tt>myForm.userEmail.$error = {{myForm.userEmail.$error}}</tt> </form> 

My problem is that $ error.uniqueEmail is always false. $ http response shows false when email is available and true when email is already registered. (adding a warning (data.isValid) confirms this.

I also tried switching the true / false statement from the $ http response without sucess (email is already registered = false and available via email = true).

What am I missing?

Edit: Ok, I found a problem

  • ctrl.$setValidity("uniqueemail", false) shows true and ctrl.$setValidity("uniqueemail", true) shows false
  • http response was {"isValid": "true"} and it should be {"isValid": true} (without quotes)
  • I switched the true / false statement from the HTTP response, so when the error should be triggered (already registered email address), the response should be {"isValid": false}
+6
source share
2 answers

There is no need for regex if you use type="email" . Angular will take care of validation for you.

 angular.module('directives.emailUnique', []) .directive('emailUnique', ['$http', function ($http) { return { restrict: 'A', require: 'ngModel', link: function (scope, el, attrs, ctrl) { ctrl.$parsers.push(function (viewValue) { if (!viewValue) { ctrl.$setValidity('emailUnique', true); return undefined; } $http.get('/api/emailunique/' + viewValue).success(function (data) { if (data.data && data.data.unique) ctrl.$setValidity('emailUnique', true); else ctrl.$setValidity('emailUnique', false); }).error(function () { alert('Sorry, a technical issue prevents to validate your email.\n ' + 'Thanks to retry later.'); }); return viewValue; }); } }; }]); 

Achieving a purely practical TDD to handle all cases, here is the specification:

 describe('emailUnique directive', function () { var $scope, form, $httpBackend; beforeEach(function () { module('directives.emailUnique'); inject(function ($compile, $rootScope, _$httpBackend_) { $scope = $rootScope; $httpBackend = _$httpBackend_; var element = angular.element( '<form name="form" novalidate><input name="email" type="email" ng-model="model.email" required email-unique/></form>' ); $scope.model = { email: '' }; $compile(element)($scope); $scope.$digest(); form = $scope.form; }); }); afterEach(function () { $httpBackend.verifyNoOutstandingExpectation(); $httpBackend.verifyNoOutstandingRequest(); }); describe('email uniqueness', function () { it('should return the current value if the email is a valid one', function () { $httpBackend.whenGET('/api/emailunique/ michael@gmail.com ').respond(200, {data: {unique: true}}); setViewValue(' michael@gmail.com '); $httpBackend.flush(); expect($scope.model.email).toBe(' michael@gmail.com '); $httpBackend.whenGET('/api/emailunique/ david@gmail.com ').respond(200, {data: {unique: true}}); setViewValue(' david@gmail.com '); $httpBackend.flush(); expect($scope.model.email).toBe(' david@gmail.com '); }); it('emailUnique validity should not be evaluated to true if email is invalid', function () { setViewValue('michael'); expect(form.email.$error.emailUnique).not.toBe(true); }); it('should set emailUnique validity to true if email is unique', function () { $httpBackend.whenGET('/api/emailunique/ michael@gmail.com ').respond(200, {data: {unique: true}}); setViewValue(' michael@gmail.com '); $httpBackend.flush(); expect(form.email.$error.emailUnique).toBe(false); }); it('should not set emailUnique validity to true if email is not unique', function(){ $httpBackend.whenGET('/api/emailunique/ david@gmail.com ').respond(200, {data: {unique: false}}); setViewValue(' david@gmail.com '); $httpBackend.flush(); expect(form.email.$error.emailUnique).toBe(true); }); it('should call emailUnique server api if email is valid, to check for uniqueness', function(){ $httpBackend.expectGET('/api/emailunique/ michael@gmail.com ').respond(200, {data: {unique: true}}); setViewValue(' michael@gmail.com '); $httpBackend.flush(); }); it('should set emailUnique to true if ' + 'it was first evaluated as not unique and then back to an invalid one', function(){ $httpBackend.whenGET('/api/emailunique/ david@gmail.com ').respond(200, {data: {unique: false}}); setViewValue(' david@gmail.com '); $httpBackend.flush(); setViewValue('michaelgmail.com'); expect(form.email.$error.emailUnique).toBe(false); }); }); function setViewValue(email) { form.email.$setViewValue(email); $scope.$digest(); } }); 
+4
source

You can also use some directive

  app.directive('uniqueEmail', ["$http", function($http) { return { require: 'ngModel', restrict: 'A', link: function(scope, elem, attrs, ctrl) { /*We need to check that the value is different to the original*/ /*using push() here to run it as the last parser, after we are sure that other validators were run*/ ctrl.$parsers.push(function(viewValue) { var filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; if (filter.test(viewValue)) { $http.get('check/e/' + viewValue).success(function(data) { //set the validity of the field ctrl.$setValidity("uniqueEmail", data.isValid); }); return viewValue; } }); } }; } ]); 
+3
source

All Articles