revert: fix(input[range]): correctly handle min/max; remove ngMin/ngMax support
This reverts commit b78539bfc0
This commit is contained in:
+51
-49
@@ -1061,11 +1061,8 @@ var inputType = {
|
||||
* Angular will also update the model value.
|
||||
*
|
||||
* Automatic value adjustment also means that a range input element can never have the `required`,
|
||||
* `min`, or `max` errors.
|
||||
*
|
||||
* Note that `input[range]` is not compatible with`ngMax` and `ngMin`, because they do not set the
|
||||
* `min` and `max` attributes, which means that the browser won't automatically adjust the input
|
||||
* value based on their values, and will always assume min = 0 and max = 100.
|
||||
* `min`, or `max` errors, except when using `ngMax` and `ngMin`, which are not affected by automatic
|
||||
* value adjustment, because they do not set the `min` and `max` attributes.
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
@@ -1073,6 +1070,14 @@ var inputType = {
|
||||
* than `min`. Can be interpolated.
|
||||
* @param {string=} max Sets the `max` validation to ensure that the value entered is less than `max`.
|
||||
* Can be interpolated.
|
||||
* @param {string=} ngMin Takes an expression. Sets the `min` validation to ensure that the value
|
||||
* entered is greater than `min`. Does not set the `min` attribute and therefore
|
||||
* adds no native HTML5 validation. It also means the browser won't adjust the
|
||||
* element value in case `min` is greater than the current value.
|
||||
* @param {string=} ngMax Takes an expression. Sets the `max` validation to ensure that the value
|
||||
* entered is less than `max`. Does not set the `max` attribute and therefore
|
||||
* adds no native HTML5 validation. It also means the browser won't adjust the
|
||||
* element value in case `max` is less than the current value.
|
||||
* @param {string=} ngChange Angular expression to be executed when the ngModel value changes due
|
||||
* to user interaction with the input element.
|
||||
*
|
||||
@@ -1542,12 +1547,10 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
numberFormatterParser(ctrl);
|
||||
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
var supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
|
||||
minVal = supportsRange ? 0 : undefined,
|
||||
maxVal = supportsRange ? 100 : undefined,
|
||||
validity = element[0].validity,
|
||||
hasMinAttr = isDefined(attr.min),
|
||||
hasMaxAttr = isDefined(attr.max);
|
||||
var minVal = 0,
|
||||
maxVal = 100,
|
||||
supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
|
||||
validity = element[0].validity;
|
||||
|
||||
var originalRender = ctrl.$render;
|
||||
|
||||
@@ -1560,39 +1563,6 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
} :
|
||||
originalRender;
|
||||
|
||||
if (hasMinAttr) {
|
||||
ctrl.$validators.min = supportsRange ?
|
||||
// Since all browsers set the input to a valid value, we don't need to check validity
|
||||
function noopMinValidator() { return true; } :
|
||||
// non-support browsers validate the range
|
||||
function minValidator(modelValue, viewValue) {
|
||||
return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
|
||||
};
|
||||
|
||||
setInitialValueAndObserver('min', minChange);
|
||||
}
|
||||
|
||||
if (hasMaxAttr) {
|
||||
ctrl.$validators.max = supportsRange ?
|
||||
// Since all browsers set the input to a valid value, we don't need to check validity
|
||||
function noopMaxValidator() { return true; } :
|
||||
// ngMax doesn't set the max attr, so the browser doesn't adjust the input value as setting max would
|
||||
function maxValidator(modelValue, viewValue) {
|
||||
return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
|
||||
};
|
||||
|
||||
setInitialValueAndObserver('max', maxChange);
|
||||
}
|
||||
|
||||
function setInitialValueAndObserver(htmlAttrName, changeFn) {
|
||||
// interpolated attributes set the attribute value only after a digest, but we need the
|
||||
// attribute value when the input is first rendered, so that the browser can adjust the
|
||||
// input value based on the min/max value
|
||||
element.attr(htmlAttrName, attr[htmlAttrName]);
|
||||
|
||||
attr.$observe(htmlAttrName, changeFn);
|
||||
}
|
||||
|
||||
function minChange(val) {
|
||||
if (isDefined(val) && !isNumber(val)) {
|
||||
val = parseFloat(val);
|
||||
@@ -1603,12 +1573,12 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (supportsRange) {
|
||||
if (supportsRange && minAttrType === 'min') {
|
||||
var elVal = element.val();
|
||||
// IE11 doesn't set the el val correctly if the minVal is greater than the element value
|
||||
if (minVal > elVal) {
|
||||
element.val(minVal);
|
||||
elVal = minVal;
|
||||
element.val(elVal);
|
||||
}
|
||||
ctrl.$setViewValue(elVal);
|
||||
} else {
|
||||
@@ -1617,6 +1587,23 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
}
|
||||
}
|
||||
|
||||
var minAttrType = isDefined(attr.ngMin) ? 'ngMin' : isDefined(attr.min) ? 'min' : false;
|
||||
if (minAttrType) {
|
||||
ctrl.$validators.min = isDefined(attr.min) && supportsRange ?
|
||||
function noopMinValidator(value) {
|
||||
// Since all browsers set the input to a valid value, we don't need to check validity
|
||||
return true;
|
||||
} :
|
||||
// ngMin doesn't set the min attr, so the browser doesn't adjust the input value as setting min would
|
||||
function minValidator(modelValue, viewValue) {
|
||||
return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
|
||||
};
|
||||
|
||||
// Assign minVal when the directive is linked. This won't run the validators as the model isn't ready yet
|
||||
minChange(attr.min);
|
||||
attr.$observe('min', minChange);
|
||||
}
|
||||
|
||||
function maxChange(val) {
|
||||
if (isDefined(val) && !isNumber(val)) {
|
||||
val = parseFloat(val);
|
||||
@@ -1627,13 +1614,12 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (supportsRange) {
|
||||
if (supportsRange && maxAttrType === 'max') {
|
||||
var elVal = element.val();
|
||||
// IE11 doesn't set the el val correctly if the maxVal is less than the element value
|
||||
if (maxVal < elVal) {
|
||||
element.val(maxVal);
|
||||
// IE11 and Chrome don't set the value to the minVal when max < min
|
||||
elVal = maxVal < minVal ? minVal : maxVal;
|
||||
elVal = minVal;
|
||||
}
|
||||
ctrl.$setViewValue(elVal);
|
||||
} else {
|
||||
@@ -1641,6 +1627,22 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
ctrl.$validate();
|
||||
}
|
||||
}
|
||||
var maxAttrType = isDefined(attr.max) ? 'max' : attr.ngMax ? 'ngMax' : false;
|
||||
if (maxAttrType) {
|
||||
ctrl.$validators.max = isDefined(attr.max) && supportsRange ?
|
||||
function noopMaxValidator() {
|
||||
// Since all browsers set the input to a valid value, we don't need to check validity
|
||||
return true;
|
||||
} :
|
||||
// ngMax doesn't set the max attr, so the browser doesn't adjust the input value as setting max would
|
||||
function maxValidator(modelValue, viewValue) {
|
||||
return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
|
||||
};
|
||||
|
||||
// Assign maxVal when the directive is linked. This won't run the validators as the model isn't ready yet
|
||||
maxChange(attr.max);
|
||||
attr.$observe('max', maxChange);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+97
-115
@@ -2819,7 +2819,7 @@ describe('input', function() {
|
||||
expect(inputElm.val()).toEqual('50');
|
||||
});
|
||||
|
||||
it('should set model to 50 when no value specified and default min/max', function() {
|
||||
it('should set model to 50 when no value specified', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="age" />');
|
||||
|
||||
expect(inputElm.val()).toBe('50');
|
||||
@@ -2829,7 +2829,7 @@ describe('input', function() {
|
||||
expect(scope.age).toBe(50);
|
||||
});
|
||||
|
||||
it('should parse non-number values to 50 when default min/max', function() {
|
||||
it('should parse non-number values to 50', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="age" />');
|
||||
|
||||
scope.$apply('age = 10');
|
||||
@@ -2891,20 +2891,8 @@ describe('input', function() {
|
||||
describe('min', function() {
|
||||
|
||||
if (supportsRange) {
|
||||
|
||||
it('should initialize correctly with non-default model and min value', function() {
|
||||
scope.value = -3;
|
||||
scope.min = -5;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="{{min}}" />');
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('-3');
|
||||
expect(scope.value).toBe(-3);
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
|
||||
// Browsers that implement range will never allow you to set the value < min values
|
||||
it('should adjust invalid input values', function() {
|
||||
it('should validate', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="10" />');
|
||||
|
||||
helper.changeInputValueTo('5');
|
||||
@@ -2918,22 +2906,6 @@ describe('input', function() {
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should set the model to the min val if it is less than the min val', function() {
|
||||
scope.value = -10;
|
||||
// Default min is 0
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="{{min}}" />');
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('0');
|
||||
expect(scope.value).toBe(0);
|
||||
|
||||
scope.$apply('value = 5; min = 10');
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('10');
|
||||
expect(scope.value).toBe(10);
|
||||
});
|
||||
|
||||
it('should adjust the element and model value when the min value changes on-the-fly', function() {
|
||||
scope.min = 10;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="{{min}}" />');
|
||||
@@ -2967,9 +2939,8 @@ describe('input', function() {
|
||||
});
|
||||
|
||||
} else {
|
||||
// input[type=range] will become type=text in browsers that don't support it
|
||||
|
||||
it('should validate if "range" is not implemented', function() {
|
||||
// This will become type=text in browsers that don't support it
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="10" />');
|
||||
|
||||
helper.changeInputValueTo('5');
|
||||
@@ -2983,34 +2954,6 @@ describe('input', function() {
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not assume a min val of 0 if the min interpolates to a non-number', function() {
|
||||
scope.value = -10;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="{{min}}" />');
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('-10');
|
||||
expect(scope.value).toBe(-10);
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
|
||||
helper.changeInputValueTo('-5');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('-5');
|
||||
expect(scope.value).toBe(-5);
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
|
||||
scope.$apply('max = "null"');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('-5');
|
||||
expect(scope.value).toBe(-5);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
|
||||
scope.$apply('max = "asdf"');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('-5');
|
||||
expect(scope.value).toBe(-5);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should validate even if the min value changes on-the-fly', function() {
|
||||
scope.min = 10;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="{{min}}" />');
|
||||
@@ -3047,21 +2990,52 @@ describe('input', function() {
|
||||
}
|
||||
});
|
||||
|
||||
describe('ngMin', function() {
|
||||
|
||||
it('should validate', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" ng-min="50" />');
|
||||
|
||||
helper.changeInputValueTo('1');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.min).toBeTruthy();
|
||||
|
||||
helper.changeInputValueTo('100');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(100);
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should validate even if the ngMin value changes on-the-fly', function() {
|
||||
scope.min = 10;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" ng-min="min" />');
|
||||
|
||||
helper.changeInputValueTo('15');
|
||||
expect(inputElm).toBeValid();
|
||||
|
||||
scope.min = 20;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.min = null;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
|
||||
scope.min = '20';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.min = 'abc';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('max', function() {
|
||||
|
||||
if (supportsRange) {
|
||||
// Browsers that implement range will never allow you to set the value > max value
|
||||
it('should initialize correctly with non-default model and max value', function() {
|
||||
scope.value = 130;
|
||||
scope.max = 150;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="{{max}}" />');
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('130');
|
||||
expect(scope.value).toBe(130);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should validate', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="10" />');
|
||||
|
||||
@@ -3076,16 +3050,9 @@ describe('input', function() {
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should set the model to the max val if it is greater than the max val', function() {
|
||||
scope.value = 110;
|
||||
// Default max is 100
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="{{max}}" />');
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('100');
|
||||
expect(scope.value).toBe(100);
|
||||
|
||||
scope.$apply('value = 90; max = 10');
|
||||
it('should set the model to the max val if it is more than the max val', function() {
|
||||
scope.value = 90;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="10" />');
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('10');
|
||||
@@ -3139,34 +3106,6 @@ describe('input', function() {
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not assume a max val of 100 if the max attribute interpolates to a non-number', function() {
|
||||
scope.value = 120;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="{{max}}" />');
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('120');
|
||||
expect(scope.value).toBe(120);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
|
||||
helper.changeInputValueTo('140');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('140');
|
||||
expect(scope.value).toBe(140);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
|
||||
scope.$apply('max = null');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('140');
|
||||
expect(scope.value).toBe(140);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
|
||||
scope.$apply('max = "asdf"');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(inputElm.val()).toBe('140');
|
||||
expect(scope.value).toBe(140);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should validate even if the max value changes on-the-fly', function() {
|
||||
scope.max = 10;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="{{max}}" />');
|
||||
@@ -3202,25 +3141,68 @@ describe('input', function() {
|
||||
}
|
||||
});
|
||||
|
||||
describe('ngMax', function() {
|
||||
|
||||
it('should validate', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" ng-max="5" />');
|
||||
|
||||
helper.changeInputValueTo('20');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(scope.form.alias.$error.max).toBeTruthy();
|
||||
|
||||
helper.changeInputValueTo('0');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(0);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should validate even if the ngMax value changes on-the-fly', function() {
|
||||
scope.max = 10;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" ng-max="max" />');
|
||||
|
||||
helper.changeInputValueTo('5');
|
||||
expect(inputElm).toBeValid();
|
||||
|
||||
scope.max = 0;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.max = null;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
|
||||
scope.max = '4';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.max = 'abc';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
if (supportsRange) {
|
||||
|
||||
describe('min and max', function() {
|
||||
|
||||
it('should set the correct initial value when min and max are specified', function() {
|
||||
it('should keep the initial default value when min and max are specified', function() {
|
||||
scope.max = 80;
|
||||
scope.min = 40;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="{{max}}" min="{{min}}" />');
|
||||
|
||||
expect(inputElm.val()).toBe('60');
|
||||
expect(scope.value).toBe(60);
|
||||
expect(inputElm.val()).toBe('50');
|
||||
expect(scope.value).toBe(50);
|
||||
});
|
||||
|
||||
|
||||
it('should set element and model value to min if max is less than min', function() {
|
||||
scope.min = 40;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="{{max}}" min="{{min}}" />');
|
||||
|
||||
expect(inputElm.val()).toBe('70');
|
||||
expect(scope.value).toBe(70);
|
||||
expect(inputElm.val()).toBe('50');
|
||||
expect(scope.value).toBe(50);
|
||||
|
||||
scope.max = 20;
|
||||
scope.$digest();
|
||||
|
||||
@@ -351,7 +351,7 @@ describe('validators', function() {
|
||||
|
||||
|
||||
it('should accept values of any length when maxlength is non-numeric', function() {
|
||||
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-maxlength="maxlength" />');
|
||||
var inputElm = helper.compileInput('<input type="text" ng-model="value" ng-maxlength="{{maxlength}}" />');
|
||||
helper.changeInputValueTo('aaaaaaaaaa');
|
||||
|
||||
$rootScope.$apply('maxlength = "5"');
|
||||
|
||||
Reference in New Issue
Block a user