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

Commit f104808

Browse files
committed
fix(input[type=range]): set model to 50 if input value defaults to 50
When the model value is undefined or null, browsers that fully implement range will set its value to 50. This causes a mismatch between the model and the view. It also means that the input cannot be "empty", that means it will never show up as required because it always has a value. This is obviously problematic if the application that won't be able to distinguish between a null value and the default value of 50. This commit sets the model to 50 after a call to $render detects that the value to be set was empty. This is done via $setViewValue, so parser and validators run. It also means that when the range model is also bound to a number input, this input can never be cleared, as clearing it will trigger the reset to 50 in the range input.
1 parent 9c49c00 commit f104808

File tree

3 files changed

+41
-17
lines changed

3 files changed

+41
-17
lines changed

src/ng/directive/input.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ var inputType = {
10921092
</file>
10931093
</example>
10941094
*/
1095-
'range': numberInputType,
1095+
'range': rangeInputType,
10961096

10971097
/**
10981098
* @ngdoc input
@@ -1498,6 +1498,24 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
14981498
}
14991499
}
15001500

1501+
function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
1502+
numberInputType(scope, element, attr, ctrl, $sniffer, $browser);
1503+
1504+
var originalRender = ctrl.$render;
1505+
1506+
ctrl.$render = function() {
1507+
console.log('custom render', ctrl.$viewValue, ctrl.$isEmpty(ctrl.$viewValue));
1508+
if (ctrl.$isEmpty(ctrl.$viewValue)) {
1509+
ctrl.$viewValue = '50';
1510+
originalRender();
1511+
ctrl.$setViewValue('50');
1512+
} else {
1513+
originalRender();
1514+
}
1515+
};
1516+
1517+
}
1518+
15011519
function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
15021520
// Note: no badInputChecker here by purpose as `url` is only a validation
15031521
// in browsers, i.e. we can always read out input.value even if it is not valid!

src/ng/directive/ngModel.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
881881
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
882882
ctrl.$render();
883883

884-
ctrl.$$runValidators(modelValue, viewValue, noop);
884+
// It is possible that model and view value have been updated during render
885+
ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop);
885886
}
886887
}
887888

test/ng/directive/inputSpec.js

+20-15
Original file line numberDiff line numberDiff line change
@@ -2825,7 +2825,7 @@ describe('input', function() {
28252825

28262826
beforeEach(function() {
28272827
scope = $rootScope;
2828-
})
2828+
});
28292829

28302830
it('should reset the model if view is invalid', function() {
28312831
var inputElm = helper.compileInput('<input type="range" ng-model="age"/>');
@@ -2852,40 +2852,45 @@ describe('input', function() {
28522852

28532853
scope.$apply('age = null');
28542854

2855-
expect(scope.age).toBeNull();
28562855
expect(inputElm.val()).toEqual('50');
28572856
});
28582857

2859-
it('should come up as 50 when no value specified', function() {
2858+
it('should set model to 50 when no value specified', function() {
28602859
var inputElm = helper.compileInput('<input type="range" ng-model="age" />');
28612860

28622861
expect(inputElm.val()).toBe('50');
28632862

28642863
scope.$apply('age = null');
28652864

2866-
expect(scope.age).toBeNull();
2867-
expect(inputElm.val()).toBe('50');
2865+
expect(scope.age).toBe(50);
28682866
});
28692867

2868+
if (msie !== 9) {
2869+
// This behavior only applies to browsers that implement the range input, which do not
2870+
// allow to set a non-number value and will set the value of the input to 50 even when you
2871+
// change it directly on the element.
2872+
// Other browsers fall back to text inputs, where setting a model value of 50 does not make
2873+
// sense if the input value is a string. These browser will mark the input as invalid instead.
2874+
it('should parse non-number values to 50', function() {
2875+
var inputElm = helper.compileInput('<input type="range" ng-model="age" />');
28702876

2871-
it('should parse empty string to 50', function() {
2872-
var inputElm = helper.compileInput('<input type="range" ng-model="age" />');
2873-
2874-
scope.$apply('age = 10');
2875-
2876-
helper.changeInputValueTo('');
2877-
expect(scope.age).toBe(50);
2878-
expect(inputElm).toBeValid();
2879-
});
2877+
scope.$apply('age = 10');
2878+
expect(inputElm.val()).toBe('10');
28802879

2880+
helper.changeInputValueTo('');
2881+
expect(scope.age).toBe(50);
2882+
expect(inputElm).toBeValid();
2883+
});
2884+
}
28812885

28822886
it('should only invalidate the model if suffering from bad input when the data is parsed', function() {
2887+
scope.age = 60;
2888+
28832889
var inputElm = helper.compileInput('<input type="range" ng-model="age" />', {
28842890
valid: false,
28852891
badInput: true
28862892
});
28872893

2888-
expect(scope.age).toBeUndefined();
28892894
expect(inputElm).toBeValid();
28902895

28912896
helper.changeInputValueTo('this-will-fail-because-of-the-badInput-flag');

0 commit comments

Comments
 (0)