Skip to content

Commit 1f6d76e

Browse files
committed
fixup! feat(forms): support type set in form validators
1 parent 65432b7 commit 1f6d76e

File tree

2 files changed

+27
-21
lines changed

2 files changed

+27
-21
lines changed

packages/forms/src/validators.ts

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,35 +25,26 @@ import type {
2525
import {RuntimeErrorCode} from './errors';
2626
import type {AbstractControl} from './model/abstract_model';
2727

28-
function isEmptyInputValue(value: any): boolean {
29-
/**
30-
* Check if the object is a string or array before evaluating the length attribute.
31-
* Check if the object is a set before evaluating the size attribute.
32-
* This avoids falsely rejecting objects that contain a custom length or size attribute.
33-
* For example, the object {id: 1, length: 0, width: 0} should not be returned as empty.
34-
*/
35-
return (
36-
value == null ||
37-
((typeof value === 'string' || Array.isArray(value)) && value.length === 0) ||
38-
(value instanceof Set && value.size === 0)
39-
);
28+
function isEmptyInputValue(value: unknown): boolean {
29+
return value == null || lengthOrSize(value) === 0;
4030
}
4131

4232
/**
43-
* Extract the length property in case it's an array.
33+
* Extract the length property in case it's an array or a string.
4434
* Extract the size property in case it's a set.
4535
* Return null else.
4636
* @param value Either an array, set or undefined.
4737
*/
48-
function lengthOrSize(value: any): number | null {
38+
function lengthOrSize(value: unknown): number | null {
4939
// non-strict comparison is intentional, to check for both `null` and `undefined` values
5040
if (value == null) {
5141
return null;
52-
} else if (typeof value.length === 'number') {
42+
} else if (Array.isArray(value) || typeof value === 'string') {
5343
return value.length;
54-
} else if (typeof value.size === 'number') {
44+
} else if (value instanceof Set) {
5545
return value.size;
5646
}
47+
5748
return null;
5849
}
5950

@@ -474,7 +465,7 @@ export class Validators {
474465
*/
475466
export function minValidator(min: number): ValidatorFn {
476467
return (control: AbstractControl): ValidationErrors | null => {
477-
if (isEmptyInputValue(control.value) || isEmptyInputValue(min)) {
468+
if (control.value == null || min == null) {
478469
return null; // don't validate empty values to allow optional controls
479470
}
480471
const value = parseFloat(control.value);
@@ -490,7 +481,7 @@ export function minValidator(min: number): ValidatorFn {
490481
*/
491482
export function maxValidator(max: number): ValidatorFn {
492483
return (control: AbstractControl): ValidationErrors | null => {
493-
if (isEmptyInputValue(control.value) || isEmptyInputValue(max)) {
484+
if (control.value == null || max == null) {
494485
return null; // don't validate empty values to allow optional controls
495486
}
496487
const value = parseFloat(control.value);
@@ -531,11 +522,14 @@ export function emailValidator(control: AbstractControl): ValidationErrors | nul
531522
/**
532523
* Validator that requires the number of items in the control's value to be greater than or equal
533524
* to the provided minimum length. See `Validators.minLength` for additional information.
525+
*
526+
* The minLengthValidator respects every length property in an object, regardless of whether it's an array.
527+
* For example, the object {id: 1, length: 0, width: 0} should be validated.
534528
*/
535529
export function minLengthValidator(minLength: number): ValidatorFn {
536530
return (control: AbstractControl): ValidationErrors | null => {
537-
const length = lengthOrSize(control.value);
538-
if (isEmptyInputValue(control.value) || !length) {
531+
const length = control.value?.length ?? lengthOrSize(control.value);
532+
if (length === null || length === 0) {
539533
// don't validate empty values to allow optional controls
540534
// don't validate values without `length` or `size` property
541535
return null;
@@ -550,10 +544,13 @@ export function minLengthValidator(minLength: number): ValidatorFn {
550544
/**
551545
* Validator that requires the number of items in the control's value to be less than or equal
552546
* to the provided maximum length. See `Validators.maxLength` for additional information.
547+
*
548+
* The maxLengthValidator respects every length property in an object, regardless of whether it's an array.
549+
* For example, the object {id: 1, length: 0, width: 0} should be validated.
553550
*/
554551
export function maxLengthValidator(maxLength: number): ValidatorFn {
555552
return (control: AbstractControl): ValidationErrors | null => {
556-
const length = lengthOrSize(control.value);
553+
const length = control.value?.length ?? lengthOrSize(control.value);
557554
if (length !== null && length > maxLength) {
558555
return {'maxlength': {'requiredLength': maxLength, 'actualLength': length}};
559556
}

packages/forms/test/validators_spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ import {normalizeValidators} from '../src/validators';
258258
expect(Validators.minLength(2)(new FormControl(undefined))).toBeNull();
259259
});
260260

261+
it('should not error on empty array', () => {
262+
expect(Validators.minLength(2)(new FormControl([]))).toBeNull();
263+
});
264+
261265
it('should not error on valid strings', () => {
262266
expect(Validators.minLength(2)(new FormControl('aa'))).toBeNull();
263267
});
@@ -308,6 +312,11 @@ import {normalizeValidators} from '../src/validators';
308312
});
309313
});
310314

315+
it('should not error on empty set', () => {
316+
const value = new Set();
317+
expect(Validators.minLength(1)(new FormControl(value))).toBeNull();
318+
});
319+
311320
it('should return null when passing a boolean', () => {
312321
expect(Validators.minLength(1)(new FormControl(true))).toBeNull();
313322
expect(Validators.minLength(1)(new FormControl(false))).toBeNull();

0 commit comments

Comments
 (0)