Skip to content

Commit e0b8985

Browse files
timlarson345crissdev
authored andcommitted
Add support for HTML5 attributes minlength and maxlength
Closes #200
1 parent 249efc1 commit e0b8985

10 files changed

Lines changed: 179 additions & 40 deletions

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,21 @@ Learn more about Custom Rules on the [WIKI](https://github.com/Knockout-Contrib/
180180
```html
181181
<input type="text" data-bind="value: myProp" step="3" />
182182
```
183-
**Special Note, the 'MinLength' attribute was removed until the HTML5 spec fully supports it**
183+
184+
**MaxLength**:
185+
186+
```html
187+
<input type="text" data-bind="value: myProp" maxlength="20" />
188+
```
189+
190+
**MinLength**:
191+
192+
```html
193+
<input type="text" data-bind="value: myProp" minlength="3" />
194+
```
195+
196+
_Note that the HTML5 attributes are lowercase ("maxlength"), whereas the
197+
Native Validation Rules are camel case ("maxLength")._
184198

185199
## Knockout Bindings
186200

dist/knockout.validation-with-locales.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ var defaults = {
6565
// make a copy so we can use 'reset' later
6666
var configuration = extend({}, defaults);
6767

68-
configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step'];
68+
configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step', 'maxlength', 'minlength'];
6969
configuration.html5InputTypes = ['email', 'number', 'date'];
7070

7171
configuration.reset = function () {
@@ -443,9 +443,9 @@ kv.configuration = configuration;
443443
return message(params, observable);
444444
}
445445
var replacements = unwrap(params);
446-
if (replacements == null) {
447-
replacements = [];
448-
}
446+
if (replacements == null) {
447+
replacements = [];
448+
}
449449
if (!utils.isArray(replacements)) {
450450
replacements = [replacements];
451451
}
@@ -567,26 +567,32 @@ kv.configuration = configuration;
567567
parseInputValidationAttributes: function (element, valueAccessor) {
568568
forEach(kv.configuration.html5Attributes, function (attr) {
569569
if (utils.hasAttribute(element, attr)) {
570-
571570
var params = element.getAttribute(attr) || true;
571+
var rule = attr;
572572

573-
if (attr === 'min' || attr === 'max')
574-
{
573+
if (attr === 'min' || attr === 'max') {
575574
// If we're validating based on the min and max attributes, we'll
576575
// need to know what the 'type' attribute is set to
577576
var typeAttr = element.getAttribute('type');
578-
if (typeof typeAttr === "undefined" || !typeAttr)
579-
{
577+
if (typeof typeAttr === "undefined" || !typeAttr) {
580578
// From http://www.w3.org/TR/html-markup/input:
581579
// An input element with no type attribute specified represents the
582580
// same thing as an input element with its type attribute set to "text".
583581
typeAttr = "text";
584582
}
585583
params = {typeAttr: typeAttr, value: params};
586584
}
585+
else if (attr === "minlength" || attr === "maxlength") {
586+
// Change params from a string to a number
587+
params = parseInt(params);
588+
589+
// The Native Rule names are Camel Case, but the HTML5 validation
590+
// attributes are all lower case
591+
rule = (attr === 'minlength') ? 'minLength' : 'maxLength';
592+
}
587593

588594
kv.addRule(valueAccessor(), {
589-
rule: attr,
595+
rule: rule,
590596
params: params
591597
});
592598
}

dist/knockout.validation-with-locales.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/knockout.validation-with-locales.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/knockout.validation.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ var defaults = {
6565
// make a copy so we can use 'reset' later
6666
var configuration = extend({}, defaults);
6767

68-
configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step'];
68+
configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step', 'maxlength', 'minlength'];
6969
configuration.html5InputTypes = ['email', 'number', 'date'];
7070

7171
configuration.reset = function () {
@@ -443,9 +443,9 @@ kv.configuration = configuration;
443443
return message(params, observable);
444444
}
445445
var replacements = unwrap(params);
446-
if (replacements == null) {
447-
replacements = [];
448-
}
446+
if (replacements == null) {
447+
replacements = [];
448+
}
449449
if (!utils.isArray(replacements)) {
450450
replacements = [replacements];
451451
}
@@ -567,26 +567,32 @@ kv.configuration = configuration;
567567
parseInputValidationAttributes: function (element, valueAccessor) {
568568
forEach(kv.configuration.html5Attributes, function (attr) {
569569
if (utils.hasAttribute(element, attr)) {
570-
571570
var params = element.getAttribute(attr) || true;
571+
var rule = attr;
572572

573-
if (attr === 'min' || attr === 'max')
574-
{
573+
if (attr === 'min' || attr === 'max') {
575574
// If we're validating based on the min and max attributes, we'll
576575
// need to know what the 'type' attribute is set to
577576
var typeAttr = element.getAttribute('type');
578-
if (typeof typeAttr === "undefined" || !typeAttr)
579-
{
577+
if (typeof typeAttr === "undefined" || !typeAttr) {
580578
// From http://www.w3.org/TR/html-markup/input:
581579
// An input element with no type attribute specified represents the
582580
// same thing as an input element with its type attribute set to "text".
583581
typeAttr = "text";
584582
}
585583
params = {typeAttr: typeAttr, value: params};
586584
}
585+
else if (attr === "minlength" || attr === "maxlength") {
586+
// Change params from a string to a number
587+
params = parseInt(params);
588+
589+
// The Native Rule names are Camel Case, but the HTML5 validation
590+
// attributes are all lower case
591+
rule = (attr === 'minlength') ? 'minLength' : 'maxLength';
592+
}
587593

588594
kv.addRule(valueAccessor(), {
589-
rule: attr,
595+
rule: rule,
590596
params: params
591597
});
592598
}

dist/knockout.validation.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/knockout.validation.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/api.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,9 @@
245245
return message(params, observable);
246246
}
247247
var replacements = ko.utils.unwrapObservable(params);
248-
if (replacements == null) {
249-
replacements = [];
250-
}
248+
if (replacements == null) {
249+
replacements = [];
250+
}
251251
if (!utils.isArray(replacements)) {
252252
replacements = [replacements];
253253
}
@@ -369,26 +369,32 @@
369369
parseInputValidationAttributes: function (element, valueAccessor) {
370370
ko.utils.arrayForEach(ko.validation.configuration.html5Attributes, function (attr) {
371371
if (utils.hasAttribute(element, attr)) {
372-
373372
var params = element.getAttribute(attr) || true;
373+
var rule = attr;
374374

375-
if (attr === 'min' || attr === 'max')
376-
{
375+
if (attr === 'min' || attr === 'max') {
377376
// If we're validating based on the min and max attributes, we'll
378377
// need to know what the 'type' attribute is set to
379378
var typeAttr = element.getAttribute('type');
380-
if (typeof typeAttr === "undefined" || !typeAttr)
381-
{
379+
if (typeof typeAttr === "undefined" || !typeAttr) {
382380
// From http://www.w3.org/TR/html-markup/input:
383381
// An input element with no type attribute specified represents the
384382
// same thing as an input element with its type attribute set to "text".
385383
typeAttr = "text";
386384
}
387385
params = {typeAttr: typeAttr, value: params};
388386
}
387+
else if (attr === "minlength" || attr === "maxlength") {
388+
// Change params from a string to a number
389+
params = parseInt(params);
390+
391+
// The Native Rule names are Camel Case, but the HTML5 validation
392+
// attributes are all lower case
393+
rule = (attr === 'minlength') ? 'minLength' : 'maxLength';
394+
}
389395

390396
ko.validation.addRule(valueAccessor(), {
391-
rule: attr,
397+
rule: rule,
392398
params: params
393399
});
394400
}

src/configuration.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var defaults = {
2828
// make a copy so we can use 'reset' later
2929
var configuration = ko.utils.extend({}, defaults);
3030

31-
configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step'];
31+
configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step', 'maxlength', 'minlength'];
3232
configuration.html5InputTypes = ['email', 'number', 'date'];
3333

3434
configuration.reset = function () {

test/validation-ui-tests.js

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -683,10 +683,10 @@ QUnit.test('Issue #481 - writeInputAttributes doesn\'t unwrap params to sync att
683683
assert.strictEqual($element.attr('min'), '15', 'min attribute is written');
684684
});
685685

686-
QUnit.test('Issue #80 - Write HTML5 Validation Attributes programmatically', function(assert) {
686+
QUnit.test('Issues #80,#200 - Write HTML5 Validation Attributes programmatically', function(assert) {
687687

688688
var vm = {
689-
testObj: ko.observable(15).extend({ min: 1, max: 100, required: true, step: 2, pattern: /blah/i })
689+
testObj: ko.observable(15).extend({ min: 1, max: 100, minLength: 5, maxLength: 25, required: true, step: 2, pattern: /blah/i })
690690
};
691691

692692
// setup the html
@@ -703,13 +703,17 @@ QUnit.test('Issue #80 - Write HTML5 Validation Attributes programmatically', fun
703703
var $el = $('#testElement');
704704
var tests = {};
705705

706-
ko.utils.arrayForEach(['required', 'min', 'max', 'step', 'pattern'], function(attr) {
706+
// The Native Rule names are Camel Case, but the HTML5 validation
707+
// attributes are all lower case
708+
ko.utils.arrayForEach(['required', 'min', 'max', 'minlength', 'maxlength', 'step', 'pattern'], function(attr) {
707709
tests[attr] = $el.attr(attr);
708710
});
709711

710712
assert.ok(tests.required, 'Required Found');
711713
assert.strictEqual(tests.min, '1', 'Min Found');
712714
assert.strictEqual(tests.max, '100', 'Max Found');
715+
assert.strictEqual(tests.minlength, '5', 'MinLength Found');
716+
assert.strictEqual(tests.maxlength, '25', 'MaxLength Found');
713717
assert.strictEqual(tests.step, '2', 'Step Found');
714718
assert.strictEqual(tests.pattern, 'blah', 'Pattern Found');
715719

@@ -845,6 +849,110 @@ QUnit.test('HTML5 Input types', function(assert) {
845849
}, 1);
846850
});
847851

852+
QUnit.test('maxlength Attribute of 8 should fail for text of length 11', function(assert) {
853+
var done = assert.async();
854+
assert.expect(3);
855+
856+
var vm = {
857+
someText: ko.validatedObservable()
858+
};
859+
860+
addTestHtml('<input id="myTestInput" type="text" maxlength="8" data-bind="value: someText, validationElement: someText" />');
861+
862+
ko.validation.init({parseInputAttributes: true}, true);
863+
applyTestBindings(vm);
864+
865+
setTimeout(function() {
866+
vm.someText('MuchTooLong'); // should fail the maxLength rule
867+
868+
var el = $('#myTestInput');
869+
870+
assert.ok(el, 'found element');
871+
assert.ok(!vm.someText.isValid(), 'Object is not valid');
872+
assert.equal(vm.someText.error(), 'Please enter no more than 8 characters.',
873+
'Message needs to be formatted correctly');
874+
875+
done();
876+
}, 1);
877+
});
878+
879+
QUnit.test('maxlength Attribute of 20 should succeed for text of length 11', function(assert) {
880+
var done = assert.async();
881+
assert.expect(2);
882+
883+
var vm = {
884+
someText: ko.validatedObservable()
885+
};
886+
887+
addTestHtml('<input id="myTestInput" type="text" maxlength="20" data-bind="value: someText, validationElement: someText" />');
888+
889+
ko.validation.init({parseInputAttributes: true}, true);
890+
applyTestBindings(vm);
891+
892+
setTimeout(function() {
893+
vm.someText('ShortEnough'); // should pass the maxLength rule
894+
895+
var el = $('#myTestInput');
896+
897+
assert.ok(el, 'found element');
898+
assert.ok(vm.someText.isValid(), 'Object is valid');
899+
900+
done();
901+
}, 1);
902+
});
903+
904+
QUnit.test('minlength Attribute of 20 should fail for text of length 8', function(assert) {
905+
var done = assert.async();
906+
assert.expect(3);
907+
908+
var vm = {
909+
someText: ko.validatedObservable()
910+
};
911+
912+
addTestHtml('<input id="myTestInput" type="text" minlength="20" data-bind="value: someText, validationElement: someText" />');
913+
914+
ko.validation.init({parseInputAttributes: true}, true);
915+
applyTestBindings(vm);
916+
917+
setTimeout(function() {
918+
vm.someText('TooShort'); // should fail the minLength rule
919+
920+
var el = $('#myTestInput');
921+
922+
assert.ok(el, 'found element');
923+
assert.ok(!vm.someText.isValid(), 'Object is not valid');
924+
assert.equal(vm.someText.error(), 'Please enter at least 20 characters.',
925+
'Message needs to be formatted correctly');
926+
927+
done();
928+
}, 1);
929+
});
930+
931+
QUnit.test('minlength Attribute of 5 should succeed for text of length 10', function(assert) {
932+
var done = assert.async();
933+
assert.expect(2);
934+
935+
var vm = {
936+
someText: ko.validatedObservable()
937+
};
938+
939+
addTestHtml('<input id="myTestInput" type="text" minlength=5 data-bind="value: someText, validationElement: someText" />');
940+
941+
ko.validation.init({parseInputAttributes: true}, true);
942+
applyTestBindings(vm);
943+
944+
setTimeout(function() {
945+
vm.someText('LongEnough'); // should pass the minLength rule
946+
947+
var el = $('#myTestInput');
948+
949+
assert.ok(el, 'found element');
950+
assert.ok(vm.someText.isValid(), 'Object is valid');
951+
952+
done();
953+
}, 1);
954+
});
955+
848956
QUnit.test('min Attribute of 20 should fail for value of 8', function(assert) {
849957
var done = assert.async();
850958
assert.expect(3);
@@ -1368,4 +1476,3 @@ QUnit.test('min Attribute of 2012-W03 should succeed for value of 2013-W01', fun
13681476
});
13691477

13701478
//#endregion
1371-

0 commit comments

Comments
 (0)