From 78f394fd17be581c84ecd526bb786ed1681d35cb Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Tue, 18 Oct 2011 02:18:00 +0300 Subject: [PATCH] feat(input): add ng:minlength and ng:maxlength validation notes(igor): I also e2e tests and refactorred the e2e test example to be more clear about what is a variable and what is an html/framework api. --- src/widget/input.js | 108 ++++++++++++++++++++++++++++++++------- test/widget/inputSpec.js | 12 +++++ 2 files changed, 102 insertions(+), 18 deletions(-) diff --git a/src/widget/input.js b/src/widget/input.js index e920733be8ae..e9c7208eb823 100644 --- a/src/widget/input.js +++ b/src/widget/input.js @@ -16,6 +16,10 @@ var INTEGER_REGEXP = /^\s*(\-|\+)?\d+\s*$/; * @param {string} ng:model Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the widgets is published. * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered. + * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than + * minlength. + * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than + * maxlength. * @param {string=} ng:pattern Sets `PATTERN` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. @@ -79,6 +83,10 @@ var INTEGER_REGEXP = /^\s*(\-|\+)?\d+\s*$/; * @param {string} ng:model Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the widgets is published. * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered. + * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than + * minlength. + * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than + * maxlength. * @param {string=} ng:pattern Sets `PATTERN` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. @@ -146,6 +154,10 @@ angularInputType('email', function() { * @param {string} ng:model Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the widgets is published. * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered. + * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than + * minlength. + * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than + * maxlength. * @param {string=} ng:pattern Sets `PATTERN` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. @@ -288,6 +300,10 @@ angularInputType('list', function() { * @param {string=} min Sets the `MIN` validation error key if the value entered is less then `min`. * @param {string=} max Sets the `MAX` validation error key if the value entered is greater then `min`. * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered. + * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than + * minlength. + * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than + * maxlength. * @param {string=} ng:pattern Sets `PATTERN` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. @@ -353,6 +369,10 @@ angularInputType('number', numericRegexpInputType(NUMBER_REGEXP, 'NUMBER')); * @param {string=} min Sets the `MIN` validation error key if the value entered is less then `min`. * @param {string=} max Sets the `MAX` validation error key if the value entered is greater then `min`. * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered. + * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than + * minlength. + * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than + * maxlength. * @param {string=} ng:pattern Sets `PATTERN` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. @@ -601,6 +621,10 @@ var HTML5_INPUTS_TYPES = makeMap( * @param {string} ng:model Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the widgets is published. * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered. + * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than + * minlength. + * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than + * maxlength. * @param {string=} ng:pattern Sets `PATTERN` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. @@ -612,32 +636,69 @@ var HTML5_INPUTS_TYPES = makeMap(
- text: - - Required! + User name: + + Required!
+ Last name: + + Too short! + + Too long!
- text = {{text}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}
+
+ user = {{user}}
+ myForm.userName.$valid = {{myForm.userName.$valid}}
+ myForm.userName.$error = {{myForm.userName.$error}}
+ myForm.lastName.$valid = {{myForm.lastName.$valid}}
+ myForm.userName.$error = {{myForm.lastName.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}
+ myForm.$error.MINLENGTH = {{!!myForm.$error.MINLENGTH}}
+ myForm.$error.MAXLENGTH = {{!!myForm.$error.MAXLENGTH}}
it('should initialize to model', function() { - expect(binding('text')).toEqual('guest'); - expect(binding('myForm.input.$valid')).toEqual('true'); + expect(binding('user')).toEqual('{\n \"last\":\"visitor",\n \"name\":\"guest\"}'); + expect(binding('myForm.userName.$valid')).toEqual('true'); + expect(binding('myForm.$valid')).toEqual('true'); }); - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); + it('should be invalid if empty when required', function() { + input('user.name').enter(''); + expect(binding('user')).toEqual('{\n \"last\":\"visitor",\n \"name\":\"\"}'); + expect(binding('myForm.userName.$valid')).toEqual('false'); + expect(binding('myForm.$valid')).toEqual('false'); + }); + + it('should be valid if empty when min length is set', function() { + input('user.last').enter(''); + expect(binding('user')).toEqual('{\n \"last\":\"",\n \"name\":\"guest\"}'); + expect(binding('myForm.lastName.$valid')).toEqual('true'); + expect(binding('myForm.$valid')).toEqual('true'); + }); + + it('should be invalid if less than required min length', function() { + input('user.last').enter('xx'); + expect(binding('user')).toEqual('{\n \"last\":\"xx",\n \"name\":\"guest\"}'); + expect(binding('myForm.lastName.$valid')).toEqual('false'); + expect(binding('myForm.lastName.$error')).toMatch(/MINLENGTH/); + expect(binding('myForm.$valid')).toEqual('false'); + }); + + it('should be valid if longer than max length', function() { + input('user.last').enter('some ridiculously long name'); + expect(binding('user')) + .toEqual('{\n \"last\":\"some ridiculously long name",\n \"name\":\"guest\"}'); + expect(binding('myForm.lastName.$valid')).toEqual('false'); + expect(binding('myForm.lastName.$error')).toMatch(/MAXLENGTH/); + expect(binding('myForm.$valid')).toEqual('false'); }); @@ -656,6 +717,8 @@ angularWidget('input', function(inputElement){ modelScope = this, patternMatch, widget, pattern = trim(inputElement.attr('ng:pattern')), + minlength = parseInt(inputElement.attr('ng:minlength'), 10), + maxlength = parseInt(inputElement.attr('ng:maxlength'), 10), loadFromScope = type.match(/^\s*\@\s*(.*)/); @@ -711,15 +774,24 @@ angularWidget('input', function(inputElement){ widget.$pristine = !(widget.$dirty = false); widget.$on('$validate', function(event) { - var $viewValue = trim(widget.$viewValue); - var inValid = widget.$required && !$viewValue; - var missMatch = $viewValue && !patternMatch($viewValue); + var $viewValue = trim(widget.$viewValue), + inValid = widget.$required && !$viewValue, + tooLong = maxlength && $viewValue && $viewValue.length > maxlength, + tooShort = minlength && $viewValue && $viewValue.length < minlength, + missMatch = $viewValue && !patternMatch($viewValue); + if (widget.$error.REQUIRED != inValid){ widget.$emit(inValid ? '$invalid' : '$valid', 'REQUIRED'); } if (widget.$error.PATTERN != missMatch){ widget.$emit(missMatch ? '$invalid' : '$valid', 'PATTERN'); } + if (widget.$error.MINLENGTH != tooShort){ + widget.$emit(tooShort ? '$invalid' : '$valid', 'MINLENGTH'); + } + if (widget.$error.MAXLENGTH != tooLong){ + widget.$emit(tooLong ? '$invalid' : '$valid', 'MAXLENGTH'); + } }); forEach(['valid', 'invalid', 'pristine', 'dirty'], function(name) { diff --git a/test/widget/inputSpec.js b/test/widget/inputSpec.js index 837d8c83190c..6524f1e3503c 100644 --- a/test/widget/inputSpec.js +++ b/test/widget/inputSpec.js @@ -550,6 +550,18 @@ describe('widget: input', function() { }); + itShouldVerify('text with ng:minlength limit', + ['', 'aaa', 'aaaaa', 'aaaaaaaaa'], + ['a', 'aa'], + {'ng:minlength': 3}); + + + itShouldVerify('text with ng:maxlength limit', + ['', 'a', 'aa', 'aaa'], + ['aaaa', 'aaaaa', 'aaaaaaaaa'], + {'ng:maxlength': 3}); + + it('should throw an error when scope pattern can\'t be found', function() { var el = jqLite(''), scope = angular.compile(el)();