Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(input): fix false initial 'number' validation error in input[number]... #9193

Closed
Closed
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
6 changes: 3 additions & 3 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -1161,16 +1161,16 @@ function badInputChecker(scope, element, attr, ctrl) {
}

function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
badInputChecker(scope, element, attr, ctrl);
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);

ctrl.$$parserName = 'number';
ctrl.$parsers.push(function(value) {
if (ctrl.$isEmpty(value)) return null;
if (NUMBER_REGEXP.test(value)) return parseFloat(value);
return undefined;
});

badInputChecker(scope, element, attr, ctrl);
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);

ctrl.$formatters.push(function(value) {
if (!ctrl.$isEmpty(value)) {
if (!isNumber(value)) {
Expand Down
119 changes: 108 additions & 11 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1140,10 +1140,8 @@ describe('ngModel', function() {

dealoc(element);
}));

});


describe('input', function() {
var formElm, inputElm, scope, $compile, $sniffer, $browser, changeInputValueTo, currentSpec;

Expand Down Expand Up @@ -2209,7 +2207,6 @@ describe('input', function() {
compileInput('<input type="text" name="input" ng-model="value" minlength="3" />');
expect(scope.value).toBe('12345');
});

});


Expand Down Expand Up @@ -3387,7 +3384,6 @@ describe('input', function() {
expect(inputElm).toBeInvalid();
});


it('should render as blank if null', function() {
compileInput('<input type="number" ng-model="age" />');

Expand All @@ -3408,7 +3404,6 @@ describe('input', function() {
expect(inputElm.val()).toBe('');
});


it('should parse empty string to null', function() {
compileInput('<input type="number" ng-model="age" />');

Expand All @@ -3419,7 +3414,6 @@ describe('input', function() {
expect(inputElm).toBeValid();
});


it('should only invalidate the model if suffering from bad input when the data is parsed', function() {
compileInput('<input type="number" ng-model="age" />', {
valid: false,
Expand All @@ -3435,7 +3429,6 @@ describe('input', function() {
expect(inputElm).toBeInvalid();
});


it('should validate number if transition from bad input to empty string', function() {
var validity = {
valid: false,
Expand All @@ -3450,14 +3443,25 @@ describe('input', function() {
expect(inputElm).toBeValid();
});

it('should be valid when no value has been set even if it is validated', function() {
// This situation isn't common, but it does arise, when other directives
// on the same element observe an attribute (e.g. ngRequired, min, max etc).

compileInput('<input type="number" name="alias" ng-model="value" />');

scope.form.alias.$validate();

expect(inputElm).toBeValid();
expect(scope.form.alias.$error.number).toBeFalsy();
});

it('should throw if the model value is not a number', function() {
expect(function() {
scope.value = 'one';
compileInput('<input type="number" ng-model="value" />');
}).toThrowMinErr('ngModel', 'numfmt', "Expected `one` to be a number");
});


describe('min', function() {

it('should validate', function() {
Expand Down Expand Up @@ -3540,7 +3544,6 @@ describe('input', function() {
});
});


describe('max', function() {

it('should validate', function() {
Expand Down Expand Up @@ -3623,7 +3626,6 @@ describe('input', function() {
});
});


describe('required', function() {

it('should be valid even if value is 0', function() {
Expand Down Expand Up @@ -3655,6 +3657,102 @@ describe('input', function() {
});
});

describe('ngRequired', function() {

describe('when the ngRequired expression initially evaluates to true', function() {

it('should be valid even if value is 0', function() {
compileInput('<input type="number" ng-model="value" name="alias" ng-required="true" />');

changeInputValueTo('0');
expect(inputElm).toBeValid();
expect(scope.value).toBe(0);
expect(scope.form.alias.$error.required).toBeFalsy();
});

it('should be valid even if value 0 is set from model', function() {
compileInput('<input type="number" ng-model="value" name="alias" ng-required="true" />');

scope.$apply('value = 0');

expect(inputElm).toBeValid();
expect(inputElm.val()).toBe('0');
expect(scope.form.alias.$error.required).toBeFalsy();
});

it('should register required on non boolean elements', function() {
compileInput('<div ng-model="value" name="alias" ng-required="true">');

scope.$apply("value = ''");

expect(inputElm).toBeInvalid();
expect(scope.form.alias.$error.required).toBeTruthy();
});

it('should change from invalid to valid when the value is empty and the ngRequired expression changes to false', function() {
compileInput('<input type="number" ng-model="value" name="alias" ng-required="ngRequiredExpr" />');

scope.$apply('ngRequiredExpr = true');

expect(inputElm).toBeInvalid();
expect(scope.value).toBeUndefined();
expect(scope.form.alias.$error.required).toBeTruthy();

scope.$apply('ngRequiredExpr = false');

expect(inputElm).toBeValid();
expect(scope.value).toBeNull();
expect(scope.form.alias.$error.required).toBeFalsy();
});
});

describe('when the ngRequired expression initially evaluates to false', function() {

it('should be valid even if value is empty', function() {
compileInput('<input type="number" ng-model="value" name="alias" ng-required="false" />');

expect(inputElm).toBeValid();
expect(scope.value).toBeNull();
expect(scope.form.alias.$error.required).toBeFalsy();
expect(scope.form.alias.$error.number).toBeFalsy();
});

it('should be valid if value is non-empty', function() {
compileInput('<input type="number" ng-model="value" name="alias" ng-required="false" />');

changeInputValueTo('42');
expect(inputElm).toBeValid();
expect(scope.value).toBe(42);
expect(scope.form.alias.$error.required).toBeFalsy();
});

it('should not register required on non boolean elements', function() {
compileInput('<div ng-model="value" name="alias" ng-required="false">');

scope.$apply("value = ''");

expect(inputElm).toBeValid();
expect(scope.form.alias.$error.required).toBeFalsy();
});

it('should change from valid to invalid when the value is empty and the ngRequired expression changes to true', function() {
compileInput('<input type="number" ng-model="value" name="alias" ng-required="ngRequiredExpr" />');

scope.$apply('ngRequiredExpr = false');

expect(inputElm).toBeValid();
expect(scope.value).toBeNull();
expect(scope.form.alias.$error.required).toBeFalsy();

scope.$apply('ngRequiredExpr = true');

expect(inputElm).toBeInvalid();
expect(scope.value).toBeUndefined();
expect(scope.form.alias.$error.required).toBeTruthy();
});
});
});

describe('minlength', function() {

it('should invalidate values that are shorter than the given minlength', function() {
Expand Down Expand Up @@ -3700,7 +3798,6 @@ describe('input', function() {
});
});


describe('maxlength', function() {

it('should invalidate values that are longer than the given maxlength', function() {
Expand Down