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

Commit 94612d6

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 #5892 Closes #9715
1 parent 14519f8 commit 94612d6

File tree

2 files changed

+316
-0
lines changed

2 files changed

+316
-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

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

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

28233083
describe('email', function() {
28243084

0 commit comments

Comments
 (0)