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

Commit b38a9c4

Browse files
Narretzpetebacondarwin
authored andcommitted
fix(input[range]): correctly handle min/max; remove ngMin/ngMax support
This commit fixes the handling of min/max, and removes support for ngMin/ngMax: min/max: Previously, interpolated min/max values on input range were not set when the first $render happens, because the interpolation directive only sets the actual element attribute value after a digest passes. That means that the browser would not adjust the input value according to min/max and the range input and model would not be initialzed as expected. With this change, input range will set the actual element attribute value during its own linking phase, as it is already available on the attrs argument passed to the link fn. ngMin/ngMax Since ng prefixed attributes do not set their corresponding element attribute, the range input would always have min = 0, and max = 100 (in supported browsers), regardless of the value in ngMin/ngMax. This is confusing and not very useful, so it's better to not support these attributes at all. The commit also fixes a test which used an interpolation inside an attribute that expects an expression. Fixes #14982 PR (#14996)
1 parent 462b3a7 commit b38a9c4

File tree

3 files changed

+165
-149
lines changed

3 files changed

+165
-149
lines changed

src/ng/directive/input.js

+49-51
Original file line numberDiff line numberDiff line change
@@ -1061,23 +1061,18 @@ var inputType = {
10611061
* Angular will also update the model value.
10621062
*
10631063
* Automatic value adjustment also means that a range input element can never have the `required`,
1064-
* `min`, or `max` errors, except when using `ngMax` and `ngMin`, which are not affected by automatic
1065-
* value adjustment, because they do not set the `min` and `max` attributes.
1064+
* `min`, or `max` errors.
1065+
*
1066+
* Note that `input[range]` is not compatible with`ngMax` and `ngMin`, because they do not set the
1067+
* `min` and `max` attributes, which means that the browser won't automatically adjust the input
1068+
* value based on their values, and will always assume min = 0 and max = 100.
10661069
*
10671070
* @param {string} ngModel Assignable angular expression to data-bind to.
10681071
* @param {string=} name Property name of the form under which the control is published.
10691072
* @param {string=} min Sets the `min` validation to ensure that the value entered is greater
10701073
* than `min`. Can be interpolated.
10711074
* @param {string=} max Sets the `max` validation to ensure that the value entered is less than `max`.
10721075
* Can be interpolated.
1073-
* @param {string=} ngMin Takes an expression. Sets the `min` validation to ensure that the value
1074-
* entered is greater than `min`. Does not set the `min` attribute and therefore
1075-
* adds no native HTML5 validation. It also means the browser won't adjust the
1076-
* element value in case `min` is greater than the current value.
1077-
* @param {string=} ngMax Takes an expression. Sets the `max` validation to ensure that the value
1078-
* entered is less than `max`. Does not set the `max` attribute and therefore
1079-
* adds no native HTML5 validation. It also means the browser won't adjust the
1080-
* element value in case `max` is less than the current value.
10811076
* @param {string=} ngChange Angular expression to be executed when the ngModel value changes due
10821077
* to user interaction with the input element.
10831078
*
@@ -1547,10 +1542,12 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
15471542
numberFormatterParser(ctrl);
15481543
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
15491544

1550-
var minVal = 0,
1551-
maxVal = 100,
1552-
supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
1553-
validity = element[0].validity;
1545+
var supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
1546+
minVal = supportsRange ? 0 : undefined,
1547+
maxVal = supportsRange ? 100 : undefined,
1548+
validity = element[0].validity,
1549+
hasMinAttr = isDefined(attr.min),
1550+
hasMaxAttr = isDefined(attr.max);
15541551

15551552
var originalRender = ctrl.$render;
15561553

@@ -1563,6 +1560,39 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
15631560
} :
15641561
originalRender;
15651562

1563+
if (hasMinAttr) {
1564+
ctrl.$validators.min = supportsRange ?
1565+
// Since all browsers set the input to a valid value, we don't need to check validity
1566+
function noopMinValidator() { return true; } :
1567+
// non-support browsers validate the range
1568+
function minValidator(modelValue, viewValue) {
1569+
return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
1570+
};
1571+
1572+
setInitialValueAndObserver('min', minChange);
1573+
}
1574+
1575+
if (hasMaxAttr) {
1576+
ctrl.$validators.max = supportsRange ?
1577+
// Since all browsers set the input to a valid value, we don't need to check validity
1578+
function noopMaxValidator() { return true; } :
1579+
// ngMax doesn't set the max attr, so the browser doesn't adjust the input value as setting max would
1580+
function maxValidator(modelValue, viewValue) {
1581+
return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
1582+
};
1583+
1584+
setInitialValueAndObserver('max', maxChange);
1585+
}
1586+
1587+
function setInitialValueAndObserver(htmlAttrName, changeFn) {
1588+
// interpolated attributes set the attribute value only after a digest, but we need the
1589+
// attribute value when the input is first rendered, so that the browser can adjust the
1590+
// input value based on the min/max value
1591+
element.attr(htmlAttrName, attr[htmlAttrName]);
1592+
1593+
attr.$observe(htmlAttrName, changeFn);
1594+
}
1595+
15661596
function minChange(val) {
15671597
if (isDefined(val) && !isNumber(val)) {
15681598
val = parseFloat(val);
@@ -1573,12 +1603,12 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
15731603
return;
15741604
}
15751605

1576-
if (supportsRange && minAttrType === 'min') {
1606+
if (supportsRange) {
15771607
var elVal = element.val();
15781608
// IE11 doesn't set the el val correctly if the minVal is greater than the element value
15791609
if (minVal > elVal) {
1580-
element.val(minVal);
15811610
elVal = minVal;
1611+
element.val(elVal);
15821612
}
15831613
ctrl.$setViewValue(elVal);
15841614
} else {
@@ -1587,23 +1617,6 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
15871617
}
15881618
}
15891619

1590-
var minAttrType = isDefined(attr.ngMin) ? 'ngMin' : isDefined(attr.min) ? 'min' : false;
1591-
if (minAttrType) {
1592-
ctrl.$validators.min = isDefined(attr.min) && supportsRange ?
1593-
function noopMinValidator(value) {
1594-
// Since all browsers set the input to a valid value, we don't need to check validity
1595-
return true;
1596-
} :
1597-
// ngMin doesn't set the min attr, so the browser doesn't adjust the input value as setting min would
1598-
function minValidator(modelValue, viewValue) {
1599-
return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
1600-
};
1601-
1602-
// Assign minVal when the directive is linked. This won't run the validators as the model isn't ready yet
1603-
minChange(attr.min);
1604-
attr.$observe('min', minChange);
1605-
}
1606-
16071620
function maxChange(val) {
16081621
if (isDefined(val) && !isNumber(val)) {
16091622
val = parseFloat(val);
@@ -1614,35 +1627,20 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
16141627
return;
16151628
}
16161629

1617-
if (supportsRange && maxAttrType === 'max') {
1630+
if (supportsRange) {
16181631
var elVal = element.val();
16191632
// IE11 doesn't set the el val correctly if the maxVal is less than the element value
16201633
if (maxVal < elVal) {
16211634
element.val(maxVal);
1622-
elVal = minVal;
1635+
// IE11 and Chrome don't set the value to the minVal when max < min
1636+
elVal = maxVal < minVal ? minVal : maxVal;
16231637
}
16241638
ctrl.$setViewValue(elVal);
16251639
} else {
16261640
// TODO(matsko): implement validateLater to reduce number of validations
16271641
ctrl.$validate();
16281642
}
16291643
}
1630-
var maxAttrType = isDefined(attr.max) ? 'max' : attr.ngMax ? 'ngMax' : false;
1631-
if (maxAttrType) {
1632-
ctrl.$validators.max = isDefined(attr.max) && supportsRange ?
1633-
function noopMaxValidator() {
1634-
// Since all browsers set the input to a valid value, we don't need to check validity
1635-
return true;
1636-
} :
1637-
// ngMax doesn't set the max attr, so the browser doesn't adjust the input value as setting max would
1638-
function maxValidator(modelValue, viewValue) {
1639-
return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
1640-
};
1641-
1642-
// Assign maxVal when the directive is linked. This won't run the validators as the model isn't ready yet
1643-
maxChange(attr.max);
1644-
attr.$observe('max', maxChange);
1645-
}
16461644

16471645
}
16481646

0 commit comments

Comments
 (0)