From c3e6502367596611ad5e0291b3a3cf1699d62124 Mon Sep 17 00:00:00 2001 From: Sebastian Janzen Date: Wed, 23 Jan 2013 13:01:38 +0100 Subject: [PATCH 1/3] feat(input.email): Input of type email support multiple attribute Added support for HTML5 attribute `multiple` to enter more than one e-mail address per input field. The mail addresses can be seperated by comma or semicolon. --- src/ng/directive/input.js | 8 +++-- test/ng/directive/inputSpec.js | 58 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index aaabd1033398..c8351cc10921 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -2,6 +2,7 @@ var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; +var EMAIL_MULTIPLE_REGEXP = /^((([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})[,;]){1,})?(\s{0,1}[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})$/; var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; var inputType = { @@ -228,7 +229,8 @@ var inputType = { * * @description * Text input with email validation. Sets the `email` validation error key if not a valid email - * address. + * address. Supports `multiple` attribute to input comma or semicolon seperated list of email + * addresses. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. @@ -590,7 +592,9 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); var emailValidator = function(value) { - if (isEmpty(value) || EMAIL_REGEXP.test(value)) { + var regexp = attr.multiple ? EMAIL_MULTIPLE_REGEXP : EMAIL_REGEXP; + if (isEmpty(value) || regexp.test(value)) { + ctrl.$setValidity('email', true); return value; } else { diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 4dcb79a38d12..ec7a993199e5 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -305,6 +305,64 @@ describe('ngModel', function() { expect(element).toBeInvalid(); expect(element).toHaveClass('ng-invalid-required'); })); + + + it('should set css classes (ng-valid, ng-invalid, ng-pristine, ng-dirty) on multiple input', + inject(function($compile, $rootScope, $sniffer) { + var element = $compile('')($rootScope); + $rootScope.$digest(); + + expect(element).toBeValid(); + expect(element).toBePristine(); + expect(element.hasClass('ng-valid-email')).toBe(true); + expect(element.hasClass('ng-invalid-email')).toBe(false); + + $rootScope.$apply(function() { + $rootScope.value = 'invalid-email'; + }); + expect(element).toBeInvalid(); + expect(element).toBePristine(); + expect(element.hasClass('ng-valid-email')).toBe(false); + expect(element.hasClass('ng-invalid-email')).toBe(true); + + element.val('invalid-again'); + browserTrigger(element, ($sniffer.hasEvent('input')) ? 'input' : 'change'); + expect(element).toBeInvalid(); + expect(element).toBeDirty(); + expect(element.hasClass('ng-valid-email')).toBe(false); + expect(element.hasClass('ng-invalid-email')).toBe(true); + + element.val('vojta@google.com'); + browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change'); + expect(element).toBeValid(); + expect(element).toBeDirty(); + expect(element.hasClass('ng-valid-email')).toBe(true); + expect(element.hasClass('ng-invalid-email')).toBe(false); + + element.val('vojta@google.com, sebastian@janzen'); + browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change'); + expect(element).toBeInvalid(); + expect(element).toBeDirty(); + expect(element.hasClass('ng-valid-email')).toBe(false); + expect(element.hasClass('ng-invalid-email')).toBe(true); + + element.val('vojta@google.com, sebastian@janzen.it'); + browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change'); + expect(element).toBeValid(); + expect(element).toBeDirty(); + expect(element.hasClass('ng-valid-email')).toBe(true); + expect(element.hasClass('ng-invalid-email')).toBe(false); + + dealoc(element); + })); + + it('should set invalid classes on init and multiple', inject(function($compile, $rootScope) { + var element = $compile('')($rootScope); + $rootScope.$digest(); + + expect(element).toBeInvalid(); + expect(element).toHaveClass('ng-invalid-required'); + })); }); From bccb4880c42ec16d64f95c55f5ba5be552ca8ae4 Mon Sep 17 00:00:00 2001 From: Sebastian Janzen Date: Wed, 23 Jan 2013 16:36:49 +0100 Subject: [PATCH 2/3] fix(input.email): Input of type email and `multiple` attribute support more than two addresses The regex did not match for more than two mail addresses. The tests have not reflected the element change. --- src/ng/directive/input.js | 2 +- test/ng/directive/inputSpec.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index c8351cc10921..0eb51018e4a6 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -2,7 +2,7 @@ var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; -var EMAIL_MULTIPLE_REGEXP = /^((([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})[,;]){1,})?(\s{0,1}[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})$/; +var EMAIL_MULTIPLE_REGEXP = /^(([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})[,;]\s{0,1}){1,}([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})$/; var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; var inputType = { diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index ec7a993199e5..764d9aaeee93 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -353,6 +353,24 @@ describe('ngModel', function() { expect(element.hasClass('ng-valid-email')).toBe(true); expect(element.hasClass('ng-invalid-email')).toBe(false); + $rootScope.$apply(function() { + $rootScope.value = 'vojta@google.com, sebastian@janzen.it, sebastian+2@janzen.'; + }); + + expect(element).toBeInvalid(); + expect(element).toBeDirty(); + expect(element.hasClass('ng-valid-email')).toBe(false); + expect(element.hasClass('ng-invalid-email')).toBe(true); + + $rootScope.$apply(function() { + $rootScope.value = 'vojta@google.com, sebastian@janzen.it, sebastian+2@janzen.it'; + }); + + expect(element).toBeValid(); + expect(element).toBeDirty(); + expect(element.hasClass('ng-valid-email')).toBe(true); + expect(element.hasClass('ng-invalid-email')).toBe(false); + dealoc(element); })); From e3b49c45492a0e7fbbbf5131e805409bea9616bc Mon Sep 17 00:00:00 2001 From: Sebastian Janzen Date: Thu, 24 Jan 2013 09:17:13 +0100 Subject: [PATCH 3/3] fix(input.email): Input of type email and `multiple` attribute validate on single address to true The regex did not match for just one mail addresses. The tests have not reflected the element change. --- src/ng/directive/input.js | 2 +- test/ng/directive/inputSpec.js | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 0eb51018e4a6..fa6d8c3bad4e 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -2,7 +2,7 @@ var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; -var EMAIL_MULTIPLE_REGEXP = /^(([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})[,;]\s{0,1}){1,}([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})$/; +var EMAIL_MULTIPLE_REGEXP = /^(([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})[,;]\s{0,1})*([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})$/; var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; var inputType = { diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 764d9aaeee93..b79411e7aa81 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -332,22 +332,25 @@ describe('ngModel', function() { expect(element.hasClass('ng-valid-email')).toBe(false); expect(element.hasClass('ng-invalid-email')).toBe(true); - element.val('vojta@google.com'); - browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change'); + $rootScope.$apply(function() { + $rootScope.value = 'vojta@google.com'; + }); expect(element).toBeValid(); expect(element).toBeDirty(); expect(element.hasClass('ng-valid-email')).toBe(true); expect(element.hasClass('ng-invalid-email')).toBe(false); - element.val('vojta@google.com, sebastian@janzen'); - browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change'); + $rootScope.$apply(function() { + $rootScope.value = 'vojta@google.com, sebastian@janzen'; + }); expect(element).toBeInvalid(); expect(element).toBeDirty(); expect(element.hasClass('ng-valid-email')).toBe(false); expect(element.hasClass('ng-invalid-email')).toBe(true); - element.val('vojta@google.com, sebastian@janzen.it'); - browserTrigger(element, $sniffer.hasEvent('input') ? 'input' : 'change'); + $rootScope.$apply(function() { + $rootScope.value = 'vojta@google.com, sebastian@janzen.it'; + }); expect(element).toBeValid(); expect(element).toBeDirty(); expect(element.hasClass('ng-valid-email')).toBe(true); @@ -356,7 +359,6 @@ describe('ngModel', function() { $rootScope.$apply(function() { $rootScope.value = 'vojta@google.com, sebastian@janzen.it, sebastian+2@janzen.'; }); - expect(element).toBeInvalid(); expect(element).toBeDirty(); expect(element.hasClass('ng-valid-email')).toBe(false); @@ -365,7 +367,6 @@ describe('ngModel', function() { $rootScope.$apply(function() { $rootScope.value = 'vojta@google.com, sebastian@janzen.it, sebastian+2@janzen.it'; }); - expect(element).toBeValid(); expect(element).toBeDirty(); expect(element.hasClass('ng-valid-email')).toBe(true);