Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow empty values to pass regex validation #241

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,17 @@ Easily disable success/error message
}]);
```

### **Allow empty values to pass regex validation**<br/>
`default: false`<br/>
To allow empty values to pass regex validation (such as: number) set allowEmptyValues to true.
This way (unlike the default behaviour), an empty input would pass a "number" validation. (same as HTML5 number input)

```javascript
.config(['$validationProvider', function ($validationProvider) {
$validationProvider.allowEmptyValues = true; // or false(default)
}]);
```

### **Multiple validators**<br/>
Use commas to separate multiple validators.

Expand Down
20 changes: 17 additions & 3 deletions dist/angular-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,18 @@ angular.module('validation.directive', ['validation.provider']);
*/
this.showErrorMessage = true;

/**
* Whether to allow for empty values to pass validation.
* When true, empty values will pass regex validations such as 'number' (vacuous truth).
* This will comply with the w3 specs for number validation.
* Otherwise, empty values will fail the regex validation (default).
* You can easily change this to true in your config
* example: $validationProvider.allowEmptyValues = true;
*
* @type {boolean}
*/
this.allowEmptyValues = false;

/**
* Check form valid, return true
* checkValid(Form): Check the specific form(Form) valid from angular `$valid`
Expand Down Expand Up @@ -308,6 +320,7 @@ angular.module('validation.directive', ['validation.provider']);
getDefaultMsg: this.getDefaultMsg,
showSuccessMessage: this.showSuccessMessage,
showErrorMessage: this.showErrorMessage,
allowEmptyValues: this.allowEmptyValues,
checkValid: this.checkValid,
validate: this.validate,
validCallback: this.validCallback,
Expand Down Expand Up @@ -603,8 +616,8 @@ angular.module('validation.directive', ['validation.provider']);

// Check with RegExp
else if (expression.constructor === RegExp) {
// Only apply the test if the value is neither undefined or null
if (value !== undefined && value !== null) {
// Only apply the test if the value is defined
if (value) {
if ($validationProvider.getExpression(validator).test(value)) {
if (validationGroup) {
groups[validationGroup][ctrl.$name] = true;
Expand All @@ -623,7 +636,8 @@ angular.module('validation.directive', ['validation.provider']);
return valid.error();
}
} else return valid.error();
}
// if the value is empty or undefined, regex pass as vacuous truth
} else return $validationProvider.allowEmptyValues ? valid.success() : valid.error();
} else return valid.error();
};

Expand Down
2 changes: 1 addition & 1 deletion dist/angular-validation.min.js

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions src/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@
*/
this.showErrorMessage = true;

/**
* Whether to allow for empty values to pass validation.
* When true, empty values will pass regex validations such as 'number' (vacuous truth).
* This will comply with the w3 specs for number validation.
* Otherwise, empty values will fail the regex validation (default).
* You can easily change this to true in your config
* example: $validationProvider.allowEmptyValues = true;
*
* @type {boolean}
*/
this.allowEmptyValues = false;

/**
* Check form valid, return true
* checkValid(Form): Check the specific form(Form) valid from angular `$valid`
Expand Down Expand Up @@ -304,6 +316,7 @@
getDefaultMsg: this.getDefaultMsg,
showSuccessMessage: this.showSuccessMessage,
showErrorMessage: this.showErrorMessage,
allowEmptyValues: this.allowEmptyValues,
checkValid: this.checkValid,
validate: this.validate,
validCallback: this.validCallback,
Expand Down
7 changes: 4 additions & 3 deletions src/validator.directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@

// Check with RegExp
else if (expression.constructor === RegExp) {
// Only apply the test if the value is neither undefined or null
if (value !== undefined && value !== null) {
// Only apply the test if the value is defined
if (value) {
if ($validationProvider.getExpression(validator).test(value)) {
if (validationGroup) {
groups[validationGroup][ctrl.$name] = true;
Expand All @@ -245,7 +245,8 @@
return valid.error();
}
} else return valid.error();
}
// if the value is empty or undefined, regex pass as vacuous truth
} else return $validationProvider.allowEmptyValues ? valid.success() : valid.error();
} else return valid.error();
};

Expand Down
48 changes: 47 additions & 1 deletion test/unit/actualValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/* jasmine specs for provider go here */

describe('provider', function() {
describe('provider with empty values disallowed', function() {
var $rootScope;
var $compile;
var $scope;
Expand Down Expand Up @@ -109,4 +109,50 @@ describe('provider', function() {
expect(successSpy).not.toHaveBeenCalled();
expect(errorSpy).toHaveBeenCalled();
}));

it('set value to undefined', inject(function() {
var submitSpy = jasmine.createSpy('submitSpy');
var successSpy = jasmine.createSpy('successSpy');
var errorSpy = jasmine.createSpy('errorSpy');

$scope.$apply(function() {
$scope.number = undefined;
});

// expect fail as a empty values are disallowed
validationProvider.validate($scope.Form)
.success(function() {
successSpy();
})
.error(function() {
errorSpy();
});

$timeout.flush();
expect(successSpy).not.toHaveBeenCalled();
expect(errorSpy).toHaveBeenCalled();
}));

it('set value to empty', inject(function() {
var submitSpy = jasmine.createSpy('submitSpy');
var successSpy = jasmine.createSpy('successSpy');
var errorSpy = jasmine.createSpy('errorSpy');

$scope.$apply(function() {
$scope.number = '';
});

// expect fail as a empty values are disallowed
validationProvider.validate($scope.Form)
.success(function() {
successSpy();
})
.error(function() {
errorSpy();
});

$timeout.flush();
expect(successSpy).not.toHaveBeenCalled();
expect(errorSpy).toHaveBeenCalled();
}));
});
93 changes: 93 additions & 0 deletions test/unit/actualValueWithEmpty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'use strict';

/* jasmine specs for provider go here */

describe('provider with empty values allowed', function() {
var $rootScope;
var $compile;
var $scope;
var $timeout;
var element;
var validationProvider;
var myApp;

beforeEach(function() {
myApp = angular.module('myApp', ['validation', 'validation.rule'])
.config(function($validationProvider) {
validationProvider = $validationProvider;
// Allow empty values
$validationProvider.allowEmptyValues = true;
});

return myApp;
});

beforeEach(module('myApp'));

beforeEach(inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$scope = $rootScope.$new();
$timeout = $injector.get('$timeout');

element = $compile('<form name="Form"><input type="number" name="numberWatch" ng-model="number" validator="number" email-error-message="Error Number" email-success-message="Good Number"/></form>')($scope);
angular.element(document.body).append(element);
$scope.$digest();
}));

it('set value to undefined', inject(function() {
var submitSpy = jasmine.createSpy('submitSpy');
var successSpy = jasmine.createSpy('successSpy');
var errorSpy = jasmine.createSpy('errorSpy');

$scope.$apply(function() {
$scope.number = undefined;
});

$scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() {
submitSpy();
});

// expect success as a vacuous truth (value is undefined and should therefore pass both number and maxlength tests)
validationProvider.validate($scope.Form)
.success(function() {
successSpy();
})
.error(function() {
errorSpy();
});

$timeout.flush();
expect(submitSpy).toHaveBeenCalled();
expect(successSpy).toHaveBeenCalled();
expect(errorSpy).not.toHaveBeenCalled();
}));

it('set value to empty', inject(function() {
var submitSpy = jasmine.createSpy('submitSpy');
var successSpy = jasmine.createSpy('successSpy');
var errorSpy = jasmine.createSpy('errorSpy');

$scope.$apply(function() {
$scope.number = '';
});

$scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() {
submitSpy();
});

// expect success as a vacuous truth (value is empty and should therefore pass both number and maxlength tests)
validationProvider.validate($scope.Form)
.success(function() {
successSpy();
})
.error(function() {
errorSpy();
});

$timeout.flush();
expect(submitSpy).toHaveBeenCalled();
expect(successSpy).toHaveBeenCalled();
expect(errorSpy).not.toHaveBeenCalled();
}));
});
16 changes: 8 additions & 8 deletions test/unit/validationGroupSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ describe('validation-group directive', function() {
it('should be dirty and invalid', function() {
$scope.Form.email.$setViewValue('foo@bar.com');
$scope.Form.telephone.$setViewValue('065839481');
$scope.Form.email.$setViewValue('');
$scope.Form.telephone.$setViewValue('');
$scope.Form.email.$setViewValue('aa');
$scope.Form.telephone.$setViewValue('aa');

expect($scope.Form.$dirty).toBe(true);
expect(element.hasClass('ng-dirty')).toBe(true);
Expand All @@ -169,11 +169,11 @@ describe('validation-group directive', function() {
expect(element.hasClass('ng-valid')).toBe(true);

$scope.Form.telephone.$setViewValue('065839481');
$scope.Form.email.$setViewValue('');
$scope.Form.email.$setViewValue('a');
expect($scope.Form.$valid).toBe(true);
expect(element.hasClass('ng-valid')).toBe(true);

$scope.Form.telephone.$setViewValue('');
$scope.Form.telephone.$setViewValue('aa');
expect($scope.Form.$valid).toBe(false);
expect(element.hasClass('ng-invalid')).toBe(true);
});
Expand All @@ -188,7 +188,7 @@ describe('validation-group directive', function() {

it('should have an error message inside the #contact element when no element is valid', function() {
$scope.Form.email.$setViewValue('foo@bar.com');
$scope.Form.email.$setViewValue('');
$scope.Form.email.$setViewValue('a');

messageElem = angular.element(element[0].querySelector('#contact > p'));
expect(messageElem.hasClass('validation-invalid')).toBe(true);
Expand All @@ -205,7 +205,7 @@ describe('validation-group directive', function() {
it('should have a success message inside the #contact element when one of element is valid', function() {
$scope.Form.email.$setViewValue('foo@bar.com');
$scope.Form.telephone.$setViewValue('065839481');
$scope.Form.email.$setViewValue('');
$scope.Form.email.$setViewValue('a');

messageElem = angular.element(element[0].querySelector('#contact > p'));
expect(messageElem.hasClass('validation-valid')).toBe(true);
Expand All @@ -214,8 +214,8 @@ describe('validation-group directive', function() {
it('should have an error message inside the #contact element when both of elements are invalid', function() {
$scope.Form.email.$setViewValue('foo@bar.com');
$scope.Form.telephone.$setViewValue('065839481');
$scope.Form.email.$setViewValue('');
$scope.Form.telephone.$setViewValue('');
$scope.Form.email.$setViewValue('a');
$scope.Form.telephone.$setViewValue('a');

messageElem = angular.element(element[0].querySelector('#contact > p'));
expect(messageElem.hasClass('validation-invalid')).toBe(true);
Expand Down