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
Regarding to @masterwork's answer. I tried that solution, but I got an error when the function tried to dig, recursively, inside a FormGroup, because there is passing a FormControl argument, instead of a FormGroup, at this line:
control.controls.forEach(c => this.markFormGroupTouched(c));
Here is my solution
markFormGroupTouched(formGroup: FormGroup) {
(<any>Object).values(formGroup.controls).forEach(control => {
if (control.controls) { // control is a FormGroup
markFormGroupTouched(control);
} else { // control is a FormControl
control.markAsTouched();
}
});
}
I made a version with some changes in the answers presented, for those who are using versions older than version 8 of the angular, I would like to share it with those who are useful.
Utility function:
import {FormControl, FormGroup} from "@angular/forms";
function getAllControls(formGroup: FormGroup): FormControl[] {
const controls: FormControl[] = [];
(<any>Object).values(formGroup.controls).forEach(control => {
if (control.controls) { // control is a FormGroup
const allControls = getAllControls(control);
controls.push(...allControls);
} else { // control is a FormControl
controls.push(control);
}
});
return controls;
}
export function isValidForm(formGroup: FormGroup): boolean {
return getAllControls(formGroup)
.filter(control => {
control.markAsTouched();
return !control.valid;
}).length === 0;
}
Usage:
onSubmit() {
if (this.isValidForm()) {
// ... TODO: logic if form is valid
}
}
I had this issue but found the "correct" way of doing so, despite it not being in any Angular tutorial I've ever found.
In your HTML, on the form
tag, add the same Template Reference Variable #myVariable='ngForm'
('hashtag' variable) that the Template-Driven Forms examples use, in addition to what the Reactive Forms examples use:
<form [formGroup]="myFormGroup" #myForm="ngForm" (ngSubmit)="submit()">
Now you have access to myForm.submitted
in the template which you can use instead of (or in addition to) myFormGroup.controls.X.touched
:
<div *ngIf="myForm.submitted" class="text-error">
<span *ngIf="myFormGroup.controls.myFieldX.errors?.badDate">invalid date format</span>
<span *ngIf="myFormGroup.controls.myFieldX.errors?.isPastDate">date cannot be in the past.</span>
</div>
Know that myForm.form === myFormGroup
is true... as long as you don't forget the ="ngForm"
part. If you use #myForm
alone, it won't work because the var will be set to the HtmlElement instead of the Directive driving that element.
Know that myFormGroup
is visible in your Component's typescript code per the Reactive Forms tutorials, but myForm
isn't, unless you pass it in through a method call, like submit(myForm)
to submit(myForm: NgForm): void {...}
. (Notice NgForm
is in title caps in the typescript but camel case in HTML.)
onSubmit(form: any): void {
if (!this.form) {
this.form.markAsTouched();
// this.form.markAsDirty(); <-- this can be useful
}
}
Here is how I do it. I don't want the error fields to show until after the submit button is pressed (or the form is touched).
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {OnInit} from "@angular/core";
export class MyFormComponent implements OnInit {
doValidation = false;
form: FormGroup;
constructor(fb: FormBuilder) {
this.form = fb.group({
title: ["", Validators.required]
});
}
ngOnInit() {
}
clickSubmitForm() {
this.doValidation = true;
if (this.form.valid) {
console.log(this.form.value);
};
}
}
<form class="form-horizontal" [formGroup]="form" >
<input type="text" class="form-control" formControlName="title">
<div *ngIf="form.get('title').hasError('required') && doValidation" class="alert alert-danger">
title is required
</div>
<button (click)="clickSubmitForm()">Submit</button>
</form>
/**
* Marks as a touched
* @param { FormGroup } formGroup
*
* @return {void}
*/
markFormGroupTouched(formGroup: FormGroup) {
Object.values(formGroup.controls).forEach((control: any) => {
if (control instanceof FormControl) {
control.markAsTouched();
control.updateValueAndValidity();
} else if (control instanceof FormGroup) {
this.markFormGroupTouched(control);
}
});
}