feat(forms): add `markAllAsTouched()` to `AbstractControl` (#26812)

Add functionality to mark a control and its descendant controls as touched

Closes #19400

PR Close #26812
This commit is contained in:
alsami 2018-10-29 09:36:17 +01:00 committed by Alex Rickabaugh
parent ab2bf83398
commit 45bf911df8
5 changed files with 133 additions and 0 deletions

View File

@ -364,6 +364,16 @@ export abstract class AbstractControl {
} }
} }
/**
* Marks the control and all its descendant controls as `touched`.
* @see `markAsTouched()`
*/
markAllAsTouched(): void {
this.markAsTouched({onlySelf: true});
this._forEachChild((control: AbstractControl) => control.markAllAsTouched());
}
/** /**
* Marks the control as `untouched`. * Marks the control as `untouched`.
* *

View File

@ -96,6 +96,58 @@ import {of } from 'rxjs';
}); });
}); });
describe('markAllAsTouched', () => {
it('should mark all descendants as touched', () => {
const formArray: FormArray = new FormArray([
new FormControl('v1'), new FormControl('v2'),
new FormGroup({'c1': new FormControl('v1')}),
new FormArray([new FormGroup({'c2': new FormControl('v2')})])
]);
expect(formArray.touched).toBe(false);
const control1 = formArray.at(0) as FormControl;
expect(control1.touched).toBe(false);
const group1 = formArray.at(2) as FormGroup;
expect(group1.touched).toBe(false);
const group1Control1 = group1.get('c1') as FormControl;
expect(group1Control1.touched).toBe(false);
const innerFormArray = formArray.at(3) as FormArray;
expect(innerFormArray.touched).toBe(false);
const innerFormArrayGroup = innerFormArray.at(0) as FormGroup;
expect(innerFormArrayGroup.touched).toBe(false);
const innerFormArrayGroupControl1 = innerFormArrayGroup.get('c2') as FormControl;
expect(innerFormArrayGroupControl1.touched).toBe(false);
formArray.markAllAsTouched();
expect(formArray.touched).toBe(true);
expect(control1.touched).toBe(true);
expect(group1.touched).toBe(true);
expect(group1Control1.touched).toBe(true);
expect(innerFormArray.touched).toBe(true);
expect(innerFormArrayGroup.touched).toBe(true);
expect(innerFormArrayGroupControl1.touched).toBe(true);
});
});
describe('setValue', () => { describe('setValue', () => {
let c: FormControl, c2: FormControl, a: FormArray; let c: FormControl, c2: FormControl, a: FormArray;

View File

@ -47,6 +47,15 @@ import {FormArray} from '@angular/forms/src/model';
expect(c.value).toBe(null); expect(c.value).toBe(null);
}); });
describe('markAllAsTouched', () => {
it('should mark only the control itself as touched', () => {
const control = new FormControl('');
expect(control.touched).toBe(false);
control.markAllAsTouched();
expect(control.touched).toBe(true);
});
});
describe('boxed values', () => { describe('boxed values', () => {
it('should support valid boxed values on creation', () => { it('should support valid boxed values on creation', () => {
const c = new FormControl({value: 'some val', disabled: true}, null !, null !); const c = new FormControl({value: 'some val', disabled: true}, null !, null !);

View File

@ -86,6 +86,67 @@ import {of } from 'rxjs';
}); });
describe('markAllAsTouched', () => {
it('should mark all descendants as touched', () => {
const formGroup: FormGroup = new FormGroup({
'c1': new FormControl('v1'),
'group': new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}),
'array': new FormArray([
new FormControl('v4'), new FormControl('v5'),
new FormGroup({'c4': new FormControl('v4')})
])
});
expect(formGroup.touched).toBe(false);
const control1 = formGroup.get('c1') as FormControl;
expect(control1.touched).toBe(false);
const innerGroup = formGroup.get('group') as FormGroup;
expect(innerGroup.touched).toBe(false);
const innerGroupFirstChildCtrl = innerGroup.get('c2') as FormControl;
expect(innerGroupFirstChildCtrl.touched).toBe(false);
formGroup.markAllAsTouched();
expect(formGroup.touched).toBe(true);
expect(control1.touched).toBe(true);
expect(innerGroup.touched).toBe(true);
expect(innerGroupFirstChildCtrl.touched).toBe(true);
const innerGroupSecondChildCtrl = innerGroup.get('c3') as FormControl;
expect(innerGroupSecondChildCtrl.touched).toBe(true);
const array = formGroup.get('array') as FormArray;
expect(array.touched).toBe(true);
const arrayFirstChildCtrl = array.at(0) as FormControl;
expect(arrayFirstChildCtrl.touched).toBe(true);
const arraySecondChildCtrl = array.at(1) as FormControl;
expect(arraySecondChildCtrl.touched).toBe(true);
const arrayFirstChildGroup = array.at(2) as FormGroup;
expect(arrayFirstChildGroup.touched).toBe(true);
const arrayFirstChildGroupFirstChildCtrl = arrayFirstChildGroup.get('c4') as FormControl;
expect(arrayFirstChildGroupFirstChildCtrl.touched).toBe(true);
});
});
describe('adding and removing controls', () => { describe('adding and removing controls', () => {
it('should update value and validity when control is added', () => { it('should update value and validity when control is added', () => {
const g = new FormGroup({'one': new FormControl('1')}); const g = new FormGroup({'one': new FormControl('1')});

View File

@ -32,6 +32,7 @@ export declare abstract class AbstractControl {
get(path: Array<string | number> | string): AbstractControl | null; get(path: Array<string | number> | string): AbstractControl | null;
getError(errorCode: string, path?: Array<string | number> | string): any; getError(errorCode: string, path?: Array<string | number> | string): any;
hasError(errorCode: string, path?: Array<string | number> | string): boolean; hasError(errorCode: string, path?: Array<string | number> | string): boolean;
markAllAsTouched(): void;
markAsDirty(opts?: { markAsDirty(opts?: {
onlySelf?: boolean; onlySelf?: boolean;
}): void; }): void;