feat(input): add support for binding to input[type=range] (#14870)
Thanks to @cironunes for the initial implementation in https://github.com/angular/angular.js/pull/9715 Adds support for binding to input[range] with the following behavior / features: - Like input[number], it requires the model to be a Number, and will set the model to a Number - it supports setting the min/max values via the min/max and ngMin/ngMax attributes - it follows the browser behavior of never allowing an invalid value. That means, when the browser converts an invalid value (empty: null, undefined, false ..., out of bounds: greater than max, less than min) to a valid value, the input will in turn set the model to this new valid value via $setViewValue. -- this means a range input will never be required and never have a non-Number model value, once the ngModel directive is initialized. -- this behavior is supported when the model changes and when the min/max attributes change in a way that prompts the browser to update the input value. -- ngMin / ngMax do not prompt the browser to update the values, as they don't set the attribute values. Instead, they will set the min / max errors when appropriate - browsers that do not support input[range] (IE9) handle the input like a number input (with validation etc.) Closes #5892 Closes #9715 Close #14870
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
@fullName Model is not of type `number`
|
||||
@description
|
||||
|
||||
The number input directive `<input type="number">` requires the model to be a `number`.
|
||||
The `input[number]` and `input[range]` directives require the model to be a `number`.
|
||||
|
||||
If the model is something else, this error will be thrown.
|
||||
|
||||
|
||||
+218
-4
@@ -1034,6 +1034,113 @@ var inputType = {
|
||||
*/
|
||||
'radio': radioInputType,
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
* @name input[range]
|
||||
*
|
||||
* @description
|
||||
* Native range input with validation and transformation.
|
||||
*
|
||||
* The model for the range input must always be a `Number`.
|
||||
*
|
||||
* IE9 and other browsers that do not support the `range` type fall back
|
||||
* to a text input. Model binding, validation and number parsing are nevertheless supported.
|
||||
*
|
||||
* Browsers that support range (latest Chrome, Safari, Firefox, Edge) treat `input[range]`
|
||||
* in a way that never allows the input to hold an invalid value. That means:
|
||||
* - any non-numerical value is set to `(max + min) / 2`.
|
||||
* - any numerical value that is less than the current min val, or greater than the current max val
|
||||
* is set to the min / max val respectively.
|
||||
*
|
||||
* This has the following consequences for Angular:
|
||||
*
|
||||
* Since the element value should always reflect the current model value, a range input
|
||||
* will set the bound ngModel expression to the value that the browser has set for the
|
||||
* input element. For example, in the following input `<input type="range" ng-model="model.value">`,
|
||||
* if the application sets `model.value = null`, the browser will set the input to `'50'`.
|
||||
* Angular will then set the model to `50`, to prevent input and model value being out of sync.
|
||||
*
|
||||
* That means the model for range will immediately be set to `50` after `ngModel` has been
|
||||
* initialized. It also means a range input can never have the required error.
|
||||
*
|
||||
* This does not only affect changes to the model value, but also to the values of the `min` and
|
||||
* `max` attributes. When these change in a way that will cause the browser to modify the input value,
|
||||
* 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, 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.
|
||||
* @param {string=} min Sets the `min` validation to ensure that the value entered is greater
|
||||
* 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.
|
||||
*
|
||||
* @example
|
||||
<example name="range-input-directive" module="rangeExample">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
angular.module('rangeExample', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.value = 75;
|
||||
$scope.min = 10;
|
||||
$scope.max = 90;
|
||||
}]);
|
||||
</script>
|
||||
<form name="myForm" ng-controller="ExampleController">
|
||||
|
||||
Model as range: <input type="range" name="range" ng-model="value" min="{{min}}" max="{{max}}">
|
||||
<hr>
|
||||
Model as number: <input type="number" ng-model="value"><br>
|
||||
Min: <input type="number" ng-model="min"><br>
|
||||
Max: <input type="number" ng-model="min"><br>
|
||||
value = <code>{{value}}</code><br/>
|
||||
myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
|
||||
myForm.range.$error = <code>{{myForm.range.$error}}</code>
|
||||
</form>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
* ## Range Input with ngMin & ngMax attributes
|
||||
|
||||
* @example
|
||||
<example name="range-input-directive-ng" module="rangeExample">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
angular.module('rangeExample', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.value = 75;
|
||||
$scope.min = 10;
|
||||
$scope.max = 90;
|
||||
}]);
|
||||
</script>
|
||||
<form name="myForm" ng-controller="ExampleController">
|
||||
Model as range: <input type="range" name="range" ng-model="value" ng-min="min" ng-max="max">
|
||||
<hr>
|
||||
Model as number: <input type="number" ng-model="value"><br>
|
||||
Min: <input type="number" ng-model="min"><br>
|
||||
Max: <input type="number" ng-model="min"><br>
|
||||
value = <code>{{value}}</code><br/>
|
||||
myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
|
||||
myForm.range.$error = <code>{{myForm.range.$error}}</code>
|
||||
</form>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
*/
|
||||
'range': rangeInputType,
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
@@ -1385,10 +1492,7 @@ 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);
|
||||
|
||||
function numberFormatterParser(ctrl) {
|
||||
ctrl.$$parserName = 'number';
|
||||
ctrl.$parsers.push(function(value) {
|
||||
if (ctrl.$isEmpty(value)) return null;
|
||||
@@ -1405,6 +1509,12 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
badInputChecker(scope, element, attr, ctrl);
|
||||
numberFormatterParser(ctrl);
|
||||
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
if (isDefined(attr.min) || attr.ngMin) {
|
||||
var minVal;
|
||||
@@ -1439,6 +1549,110 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
}
|
||||
}
|
||||
|
||||
function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
badInputChecker(scope, element, attr, ctrl);
|
||||
numberFormatterParser(ctrl);
|
||||
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
var minVal = 0,
|
||||
maxVal = 100,
|
||||
supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
|
||||
validity = element[0].validity;
|
||||
|
||||
var originalRender = ctrl.$render;
|
||||
|
||||
ctrl.$render = supportsRange && isDefined(validity.rangeUnderflow) && isDefined(validity.rangeOverflow) ?
|
||||
//Browsers that implement range will set these values automatically, but reading the adjusted values after
|
||||
//$render would cause the min / max validators to be applied with the wrong value
|
||||
function rangeRender() {
|
||||
originalRender();
|
||||
ctrl.$setViewValue(element.val());
|
||||
} :
|
||||
originalRender;
|
||||
|
||||
function minChange(val) {
|
||||
if (isDefined(val) && !isNumber(val)) {
|
||||
val = parseFloat(val);
|
||||
}
|
||||
minVal = isNumber(val) && !isNaN(val) ? val : undefined;
|
||||
// ignore changes before model is initialized
|
||||
if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
ctrl.$setViewValue(elVal);
|
||||
} else {
|
||||
// TODO(matsko): implement validateLater to reduce number of validations
|
||||
ctrl.$validate();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
|
||||
// ignore changes before model is initialized
|
||||
if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
elVal = minVal;
|
||||
}
|
||||
ctrl.$setViewValue(elVal);
|
||||
} else {
|
||||
// TODO(matsko): implement validateLater to reduce number of validations
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
// Note: no badInputChecker here by purpose as `url` is only a validation
|
||||
// in browsers, i.e. we can always read out input.value even if it is not valid!
|
||||
|
||||
@@ -881,7 +881,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
|
||||
ctrl.$render();
|
||||
|
||||
ctrl.$$runValidators(modelValue, viewValue, noop);
|
||||
// It is possible that model and view value have been updated during render
|
||||
ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2819,6 +2819,430 @@ describe('input', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('range', function() {
|
||||
|
||||
var scope;
|
||||
|
||||
var rangeTestEl = angular.element('<input type="range">');
|
||||
var supportsRange = rangeTestEl[0].type === 'range';
|
||||
beforeEach(function() {
|
||||
scope = $rootScope;
|
||||
});
|
||||
|
||||
if (supportsRange) {
|
||||
// This behavior only applies to browsers that implement the range input, which do not
|
||||
// allow to set a non-number value and will set the value of the input to 50 even when you
|
||||
// change it directly on the element.
|
||||
// Other browsers fall back to text inputs, where setting a model value of 50 does not make
|
||||
// sense if the input value is a string. These browsers will mark the input as invalid instead.
|
||||
|
||||
it('should render as 50 if null', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="age" />');
|
||||
|
||||
helper.changeInputValueTo('25');
|
||||
expect(scope.age).toBe(25);
|
||||
|
||||
scope.$apply('age = null');
|
||||
|
||||
expect(inputElm.val()).toEqual('50');
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
scope.$apply('age = null');
|
||||
|
||||
expect(scope.age).toBe(50);
|
||||
});
|
||||
|
||||
it('should parse non-number values to 50', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="age" />');
|
||||
|
||||
scope.$apply('age = 10');
|
||||
expect(inputElm.val()).toBe('10');
|
||||
|
||||
helper.changeInputValueTo('');
|
||||
expect(scope.age).toBe(50);
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
it('should reset the model if view is invalid', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="age"/>');
|
||||
|
||||
scope.$apply('age = 100');
|
||||
expect(inputElm.val()).toBe('100');
|
||||
|
||||
helper.changeInputValueTo('100X');
|
||||
expect(inputElm.val()).toBe('100X');
|
||||
expect(scope.age).toBeUndefined();
|
||||
expect(inputElm).toBeInvalid();
|
||||
});
|
||||
}
|
||||
|
||||
it('should parse the input value to a Number', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="age" />');
|
||||
|
||||
helper.changeInputValueTo('75');
|
||||
expect(scope.age).toBe(75);
|
||||
});
|
||||
|
||||
|
||||
it('should only invalidate the model if suffering from bad input when the data is parsed', function() {
|
||||
scope.age = 60;
|
||||
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="age" />', {
|
||||
valid: false,
|
||||
badInput: true
|
||||
});
|
||||
|
||||
expect(inputElm).toBeValid();
|
||||
|
||||
helper.changeInputValueTo('this-will-fail-because-of-the-badInput-flag');
|
||||
|
||||
expect(scope.age).toBeUndefined();
|
||||
expect(inputElm).toBeInvalid();
|
||||
});
|
||||
|
||||
|
||||
it('should throw if the model value is not a number', function() {
|
||||
expect(function() {
|
||||
scope.value = 'one';
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" />');
|
||||
}).toThrowMinErr('ngModel', 'numfmt', 'Expected `one` to be a number');
|
||||
});
|
||||
|
||||
|
||||
describe('min', function() {
|
||||
|
||||
if (supportsRange) {
|
||||
// Browsers that implement range will never allow you to set the value < min values
|
||||
it('should validate', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="10" />');
|
||||
|
||||
helper.changeInputValueTo('5');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(10);
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
|
||||
helper.changeInputValueTo('100');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(100);
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
|
||||
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}}" />');
|
||||
|
||||
helper.changeInputValueTo('15');
|
||||
expect(inputElm).toBeValid();
|
||||
|
||||
scope.min = 20;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(20);
|
||||
expect(inputElm.val()).toBe('20');
|
||||
|
||||
scope.min = null;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(20);
|
||||
expect(inputElm.val()).toBe('20');
|
||||
|
||||
scope.min = '15';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(20);
|
||||
expect(inputElm.val()).toBe('20');
|
||||
|
||||
scope.min = 'abc';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(20);
|
||||
expect(inputElm.val()).toBe('20');
|
||||
});
|
||||
|
||||
} else {
|
||||
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');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeUndefined();
|
||||
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 min value changes on-the-fly', function() {
|
||||
scope.min = 10;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" min="{{min}}" />');
|
||||
|
||||
helper.changeInputValueTo('15');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(15);
|
||||
|
||||
scope.min = 20;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(inputElm.val()).toBe('15');
|
||||
|
||||
scope.min = null;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(15);
|
||||
expect(inputElm.val()).toBe('15');
|
||||
|
||||
scope.min = '16';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(inputElm.val()).toBe('15');
|
||||
|
||||
scope.min = 'abc';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(15);
|
||||
expect(inputElm.val()).toBe('15');
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
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 validate', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="10" />');
|
||||
|
||||
helper.changeInputValueTo('20');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(10);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
|
||||
helper.changeInputValueTo('0');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(0);
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
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');
|
||||
expect(scope.value).toBe(10);
|
||||
});
|
||||
|
||||
it('should adjust the element and model value 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}}" />');
|
||||
|
||||
helper.changeInputValueTo('5');
|
||||
expect(inputElm).toBeValid();
|
||||
|
||||
scope.max = 0;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(0);
|
||||
expect(inputElm.val()).toBe('0');
|
||||
|
||||
scope.max = null;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(0);
|
||||
expect(inputElm.val()).toBe('0');
|
||||
|
||||
scope.max = '4';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(0);
|
||||
expect(inputElm.val()).toBe('0');
|
||||
|
||||
scope.max = 'abc';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(0);
|
||||
expect(inputElm.val()).toBe('0');
|
||||
});
|
||||
|
||||
} else {
|
||||
it('should validate if "range" is not implemented', function() {
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="10" />');
|
||||
|
||||
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 max value changes on-the-fly', function() {
|
||||
scope.max = 10;
|
||||
var inputElm = helper.compileInput('<input type="range" ng-model="value" name="alias" max="{{max}}" />');
|
||||
|
||||
helper.changeInputValueTo('5');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(5);
|
||||
|
||||
scope.max = 0;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(inputElm.val()).toBe('5');
|
||||
|
||||
scope.max = null;
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(5);
|
||||
expect(inputElm.val()).toBe('5');
|
||||
|
||||
scope.max = '4';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(inputElm.val()).toBe('5');
|
||||
|
||||
scope.max = 'abc';
|
||||
scope.$digest();
|
||||
expect(inputElm).toBeValid();
|
||||
expect(scope.value).toBe(5);
|
||||
expect(inputElm.val()).toBe('5');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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 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('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('50');
|
||||
expect(scope.value).toBe(50);
|
||||
|
||||
scope.max = 20;
|
||||
scope.$digest();
|
||||
|
||||
expect(inputElm.val()).toBe('40');
|
||||
expect(scope.value).toBe(40);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
describe('email', function() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user