I am having trouble finding out how to mark all form\'s fields as touched. The main problem is that if I do not touch fields and try to submit form - validation error in not
I completely understand the OP's frustration. I use the following:
Utility function:
/**
* Determines if the given form is valid by touching its controls
* and updating their validity.
* @param formGroup the container of the controls to be checked
* @returns {boolean} whether or not the form was invalid.
*/
export function formValid(formGroup: FormGroup): boolean {
return !Object.keys(formGroup.controls)
.map(controlName => formGroup.controls[controlName])
.filter(control => {
control.markAsTouched();
control.updateValueAndValidity();
return !control.valid;
}).length;
}
Usage:
onSubmit() {
if (!formValid(this.formGroup)) {
return;
}
// ... TODO: logic if form is valid.
}
Note that this function does not yet cater for nested controls.
View:
<button (click)="Submit(yourFormGroup)">Submit</button>
API
Submit(form: any) {
if (form.status === 'INVALID') {
for (let inner in details.controls) {
details.get(inner).markAsTouched();
}
return false;
}
// as it return false it breaks js execution and return
This is the code that I am actually using.
validateAllFormFields(formGroup: any) {
// This code also works in IE 11
Object.keys(formGroup.controls).forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({ onlySelf: true });
} else if (control instanceof FormGroup) {
this.validateAllFormFields(control);
} else if (control instanceof FormArray) {
this.validateAllFormFields(control);
}
});
}
This code works for me:
markAsRequired(formGroup: FormGroup) {
if (Reflect.getOwnPropertyDescriptor(formGroup, 'controls')) {
(<any>Object).values(formGroup.controls).forEach(control => {
if (control instanceof FormGroup) {
// FormGroup
markAsRequired(control);
}
// FormControl
control.markAsTouched();
});
}
}
Looping through the form controls and marking them as touched would also work:
for(let i in this.form.controls)
this.form.controls[i].markAsTouched();
For those worried about performance, I've come up with a solution that doesn't use recursion, although it still iterates over all controls in all levels.
/**
* Iterates over a FormGroup or FormArray and mark all controls as
* touched, including its children.
*
* @param {(FormGroup | FormArray)} rootControl - Root form
* group or form array
* @param {boolean} [visitChildren=true] - Specify whether it should
* iterate over nested controls
*/
public markControlsAsTouched(rootControl: FormGroup | FormArray,
visitChildren: boolean = true) {
let stack: (FormGroup | FormArray)[] = [];
// Stack the root FormGroup or FormArray
if (rootControl &&
(rootControl instanceof FormGroup || rootControl instanceof FormArray)) {
stack.push(rootControl);
}
while (stack.length > 0) {
let currentControl = stack.pop();
(<any>Object).values(currentControl.controls).forEach((control) => {
// If there are nested forms or formArrays, stack them to visit later
if (visitChildren &&
(control instanceof FormGroup || control instanceof FormArray)
) {
stack.push(control);
} else {
control.markAsTouched();
}
});
}
}
This solution works form both FormGroup and also FormArray.
You can play around with it here: angular-mark-as-touched