Skip to content

Commit 58073f8

Browse files
cironunesNarretz
authored andcommitted
feat(input): add support for input[type=range]
the input[type=range] behavior is the same of an input[type=number] with min=0, max=100 and step=1 as defaults Closes angular#5892 Closes angular#9715
1 parent 14519f8 commit 58073f8

File tree

2 files changed

+310
-0
lines changed

2 files changed

+310
-0
lines changed

src/ng/directive/input.js

+56
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,62 @@ var inputType = {
10311031
*/
10321032
'radio': radioInputType,
10331033

1034+
/**
1035+
* @ngdoc input
1036+
* @name input[range]
1037+
*
1038+
* @description
1039+
* Native range input with number validation and transformation. Sets the `number` validation
1040+
* to always have a valid number.
1041+
*
1042+
* @param {string} ngModel Assignable angular expression to data-bind to.
1043+
* @param {string=} name Property name of the form under which the control is published.
1044+
* @param {string=} min Sets the `min` validation to ensure that the value entered is greater the `min`.
1045+
* @param {string=} max Sets the `max` validation to ensure that the value entered is lesser than `max`.
1046+
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
1047+
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
1048+
* patterns defined as scope expressions.
1049+
* @param {string=} ngChange Angular expression to be executed when input changes due to user
1050+
* interaction with the input element.
1051+
*
1052+
* @example
1053+
<example name="range-input-directive" module="rangeExample">
1054+
<file name="index.html">
1055+
<script>
1056+
angular.module('rangeExample', [])
1057+
.controller('ExampleController', ['$scope', function($scope) {
1058+
$scope.value = 50;
1059+
}]);
1060+
</script>
1061+
<form name="myForm" ng-controller="ExampleController">
1062+
Number: <input type="range" name="input" ng-model="value"
1063+
min="0" max="100">
1064+
<tt>value = {{value}}</tt><br/>
1065+
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
1066+
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
1067+
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
1068+
</form>
1069+
</file>
1070+
<file name="protractor.js" type="protractor">
1071+
var value = element(by.binding('value'));
1072+
var valid = element(by.binding('myForm.input.$valid'));
1073+
var input = element(by.model('value'));
1074+
1075+
it('should initialize to model', function() {
1076+
expect(value.getText()).toContain('50');
1077+
expect(valid.getText()).toContain('true');
1078+
});
1079+
1080+
it('should respect the max', function() {
1081+
input.clear();
1082+
input.sendKeys('123');
1083+
expect(value.getText()).toEqual('value = 100');
1084+
expect(valid.getText()).toContain('true');
1085+
});
1086+
</file>
1087+
</example>
1088+
*/
1089+
'range': numberInputType,
10341090

10351091
/**
10361092
* @ngdoc input

test/ng/directive/inputSpec.js

+254
Original file line numberDiff line numberDiff line change
@@ -2819,6 +2819,260 @@ describe('input', function() {
28192819
});
28202820
});
28212821

2822+
describe('range', function() {
2823+
2824+
it('should reset the model if view is invalid', function() {
2825+
compileInput('<input type="range" ng-model="age"/>');
2826+
2827+
scope.$apply('age = 100');
2828+
expect(inputElm.val()).toBe('100');
2829+
2830+
try {
2831+
// to allow non-number values, we have to change type so that
2832+
// the browser which have number validation will not interfere with
2833+
// this test. IE8 won't allow it hence the catch.
2834+
inputElm[0].setAttribute('type', 'text');
2835+
} catch (e) {}
2836+
2837+
changeInputValueTo('100X');
2838+
expect(inputElm.val()).toBe('100X');
2839+
expect(scope.age).toBeUndefined();
2840+
expect(inputElm).toBeInvalid();
2841+
});
2842+
2843+
2844+
it('should render as 50 if null', function() {
2845+
compileInput('<input type="range" ng-model="age" />');
2846+
2847+
scope.$apply('age = null');
2848+
2849+
expect(scope.age).toBeNull();
2850+
expect(inputElm.val()).toEqual('50');
2851+
});
2852+
2853+
it('should come up as 50 when no value specified', function() {
2854+
compileInput('<input type="range" ng-model="age" />');
2855+
2856+
expect(inputElm.val()).toBe('50');
2857+
2858+
scope.$apply('age = null');
2859+
2860+
expect(scope.age).toBeNull();
2861+
expect(inputElm.val()).toBe('50');
2862+
});
2863+
2864+
2865+
it('should parse empty string to 50', function() {
2866+
compileInput('<input type="range" ng-model="age" />');
2867+
2868+
scope.$apply('age = 10');
2869+
2870+
changeInputValueTo('');
2871+
expect(scope.age).toBe(50);
2872+
expect(inputElm).toBeValid();
2873+
});
2874+
2875+
2876+
it('should only invalidate the model if suffering from bad input when the data is parsed', function() {
2877+
compileInput('<input type="range" ng-model="age" />', {
2878+
valid: false,
2879+
badInput: true
2880+
});
2881+
2882+
expect(scope.age).toBeUndefined();
2883+
expect(inputElm).toBeValid();
2884+
2885+
changeInputValueTo('this-will-fail-because-of-the-badInput-flag');
2886+
2887+
expect(scope.age).toBeUndefined();
2888+
expect(inputElm).toBeInvalid();
2889+
});
2890+
2891+
2892+
it('should validate with undefined viewValue when $validate() called', function() {
2893+
compileInput('<input type="range" name="alias" ng-model="value" />');
2894+
2895+
scope.form.alias.$validate();
2896+
2897+
expect(inputElm).toBeValid();
2898+
expect(scope.form.alias.$error.number).toBeUndefined();
2899+
});
2900+
2901+
2902+
it('should throw if the model value is not a number', function() {
2903+
expect(function() {
2904+
scope.value = 'one';
2905+
compileInput('<input type="range" ng-model="value" />');
2906+
}).toThrowMinErr('ngModel', 'numfmt', "Expected `one` to be a number");
2907+
});
2908+
2909+
2910+
describe('min', function() {
2911+
2912+
it('should validate', function() {
2913+
compileInput('<input type="range" ng-model="value" name="alias" min="10" />');
2914+
2915+
changeInputValueTo('5');
2916+
expect(inputElm).toBeValid();
2917+
expect(scope.value).toBe(10);
2918+
expect(scope.form.alias.$error.min).toBeFalsy();
2919+
2920+
changeInputValueTo('100');
2921+
expect(inputElm).toBeValid();
2922+
expect(scope.value).toBe(100);
2923+
expect(scope.form.alias.$error.min).toBeFalsy();
2924+
});
2925+
2926+
it('should validate even if min value changes on-the-fly', function() {
2927+
scope.min = 10;
2928+
compileInput('<input type="range" ng-model="value" name="alias" min="{{min}}" />');
2929+
2930+
changeInputValueTo('15');
2931+
expect(inputElm).toBeValid();
2932+
2933+
scope.min = 20;
2934+
scope.$digest();
2935+
expect(inputElm).toBeInvalid();
2936+
2937+
scope.min = null;
2938+
scope.$digest();
2939+
expect(inputElm).toBeValid();
2940+
2941+
scope.min = '20';
2942+
scope.$digest();
2943+
expect(inputElm).toBeInvalid();
2944+
2945+
scope.min = 'abc';
2946+
scope.$digest();
2947+
expect(inputElm).toBeValid();
2948+
});
2949+
});
2950+
2951+
describe('ngMin', function() {
2952+
2953+
it('should validate', function() {
2954+
compileInput('<input type="range" ng-model="value" name="alias" ng-min="50" />');
2955+
2956+
changeInputValueTo('1');
2957+
expect(inputElm).toBeInvalid();
2958+
expect(scope.value).toBeFalsy();
2959+
expect(scope.form.alias.$error.min).toBeTruthy();
2960+
2961+
changeInputValueTo('100');
2962+
expect(inputElm).toBeValid();
2963+
expect(scope.value).toBe(100);
2964+
expect(scope.form.alias.$error.min).toBeFalsy();
2965+
});
2966+
2967+
it('should validate even if the ngMin value changes on-the-fly', function() {
2968+
scope.min = 10;
2969+
compileInput('<input type="range" ng-model="value" name="alias" ng-min="min" />');
2970+
2971+
changeInputValueTo('15');
2972+
expect(inputElm).toBeValid();
2973+
2974+
scope.min = 20;
2975+
scope.$digest();
2976+
expect(inputElm).toBeInvalid();
2977+
2978+
scope.min = null;
2979+
scope.$digest();
2980+
expect(inputElm).toBeValid();
2981+
2982+
scope.min = '20';
2983+
scope.$digest();
2984+
expect(inputElm).toBeInvalid();
2985+
2986+
scope.min = 'abc';
2987+
scope.$digest();
2988+
expect(inputElm).toBeValid();
2989+
});
2990+
});
2991+
2992+
2993+
describe('max', function() {
2994+
2995+
it('should validate', function() {
2996+
compileInput('<input type="range" ng-model="value" name="alias" max="10" />');
2997+
2998+
changeInputValueTo('20');
2999+
expect(inputElm).toBeValid();
3000+
expect(scope.value).toBe(10);
3001+
expect(scope.form.alias.$error.max).toBeFalsy();
3002+
3003+
changeInputValueTo('0');
3004+
expect(inputElm).toBeValid();
3005+
expect(scope.value).toBe(0);
3006+
expect(scope.form.alias.$error.max).toBeFalsy();
3007+
});
3008+
3009+
it('should validate even if max value changes on-the-fly', function() {
3010+
scope.max = 10;
3011+
compileInput('<input type="range" ng-model="value" name="alias" max="{{max}}" />');
3012+
3013+
changeInputValueTo('5');
3014+
expect(inputElm).toBeValid();
3015+
3016+
scope.max = 0;
3017+
scope.$digest();
3018+
expect(inputElm).toBeInvalid();
3019+
3020+
scope.max = null;
3021+
scope.$digest();
3022+
expect(inputElm).toBeValid();
3023+
3024+
scope.max = '4';
3025+
scope.$digest();
3026+
expect(inputElm).toBeInvalid();
3027+
3028+
scope.max = 'abc';
3029+
scope.$digest();
3030+
expect(inputElm).toBeValid();
3031+
});
3032+
});
3033+
3034+
describe('ngMax', function() {
3035+
3036+
it('should validate', function() {
3037+
compileInput('<input type="range" ng-model="value" name="alias" ng-max="5" />');
3038+
3039+
changeInputValueTo('20');
3040+
expect(inputElm).toBeInvalid();
3041+
expect(scope.value).toBeUndefined();
3042+
expect(scope.form.alias.$error.max).toBeTruthy();
3043+
3044+
changeInputValueTo('0');
3045+
expect(inputElm).toBeValid();
3046+
expect(scope.value).toBe(0);
3047+
expect(scope.form.alias.$error.max).toBeFalsy();
3048+
});
3049+
3050+
it('should validate even if the ngMax value changes on-the-fly', function() {
3051+
scope.max = 10;
3052+
compileInput('<input type="range" ng-model="value" name="alias" ng-max="max" />');
3053+
3054+
changeInputValueTo('5');
3055+
expect(inputElm).toBeValid();
3056+
3057+
scope.max = 0;
3058+
scope.$digest();
3059+
expect(inputElm).toBeInvalid();
3060+
3061+
scope.max = null;
3062+
scope.$digest();
3063+
expect(inputElm).toBeValid();
3064+
3065+
scope.max = '4';
3066+
scope.$digest();
3067+
expect(inputElm).toBeInvalid();
3068+
3069+
scope.max = 'abc';
3070+
scope.$digest();
3071+
expect(inputElm).toBeValid();
3072+
});
3073+
});
3074+
3075+
});
28223076

28233077
describe('email', function() {
28243078

0 commit comments

Comments
 (0)