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 ran into the same problem, but I do not want to "pollute" my components with code that handles this. Especially since I need this in many forms and I do not want to repeat the code on various occasions.
Thus I created a directive (using the answers posted so far). The directive decorates NgForm's onSubmit
-Method: If the form is invalid it marks all fields as touched and aborts submission. Otherwise the usual onSubmit-Method executes normally.
import {Directive, Host} from '@angular/core';
import {NgForm} from '@angular/forms';
@Directive({
selector: '[appValidateOnSubmit]'
})
export class ValidateOnSubmitDirective {
constructor(@Host() form: NgForm) {
const oldSubmit = form.onSubmit;
form.onSubmit = function (): boolean {
if (form.invalid) {
const controls = form.controls;
Object.keys(controls).forEach(controlName => controls[controlName].markAsTouched());
return false;
}
return oldSubmit.apply(form, arguments);
};
}
}
Usage:
<form (ngSubmit)="submit()" appValidateOnSubmit>
<!-- ... form controls ... -->
</form>
From Angular 8/9 you can simply use
this.form.markAllAsTouched();
To mark a control and it's descendant controls as touched.
AbstractControl doc
as per @masterwork
typescript code for the angular version 8
private markFormGroupTouched(formGroup: FormGroup) {
(Object as any).values(formGroup.controls).forEach(control => {
control.markAsTouched();
if (control.controls) {
this.markFormGroupTouched(control);
}
}); }
See this gem. So far the most elegant solution I've seen.
Full code
import { Injectable } from '@angular/core';
import { FormGroup } from '@angular/forms';
const TOUCHED = 'markAsTouched';
const UNTOUCHED = 'markAsUntouched';
const DIRTY = 'markAsDirty';
const PENDING = 'markAsPending';
const PRISTINE = 'markAsPristine';
const FORM_CONTROL_STATES: Array<string> = [TOUCHED, UNTOUCHED, DIRTY, PENDING, PRISTINE];
@Injectable({
providedIn: 'root'
})
export class FormStateService {
markAs (form: FormGroup, state: string): FormGroup {
if (FORM_CONTROL_STATES.indexOf(state) === -1) {
return form;
}
const controls: Array<string> = Object.keys(form.controls);
for (const control of controls) {
form.controls[control][state]();
}
return form;
}
markAsTouched (form: FormGroup): FormGroup {
return this.markAs(form, TOUCHED);
}
markAsUntouched (form: FormGroup): FormGroup {
return this.markAs(form, UNTOUCHED);
}
markAsDirty (form: FormGroup): FormGroup {
return this.markAs(form, DIRTY);
}
markAsPending (form: FormGroup): FormGroup {
return this.markAs(form, PENDING);
}
markAsPristine (form: FormGroup): FormGroup {
return this.markAs(form, PRISTINE);
}
}
The following function recurses through controls in a form group and gently touches them. Because the controls field is an object, the code call Object.values() on the form group's control field.
/**
* Marks all controls in a form group as touched
* @param formGroup - The form group to touch
*/
private markFormGroupTouched(formGroup: FormGroup) {
(<any>Object).values(formGroup.controls).forEach(control => {
control.markAsTouched();
if (control.controls) {
this.markFormGroupTouched(control);
}
});
}
From Angular v8, you have this built-in with the help of the markAllAsTouched
method.
As an example, you could use it like
form.markAllAsTouched();
See the official doc : https://angular.io/api/forms/AbstractControl#markallastouched