Hello StackOverflow community!
I'm developing an app in Angular + Node and I've encountered a big issue regarding forms and validators using Material Angular.
At the moment, I'm stuck with the HTML component not recognizing the input, and I don't know what I'm missing.
I have this code:
contact.component.html:
<mat-card>
<mat-card-header>
<mat-card-title>Formulario de contacto</mat-card-title>
<mat-card-subtitle>Ponte en contacto conmigo</mat-card-subtitle>
</mat-card-header>
<form [formGroup]="contactForm">
<mat-card-content>
<mat-form-field>
<mat-label>Nombre y apellidos</mat-label>
<input matInput [formGroup]="contactForm" [formControl]="nameFormControl" required name="name" maxlength="128" autocomplete="off" autofocus>
@if (nameFormControl.hasError('required') || nameFormControl.hasError('pattern')) {
<mat-error>Por favor, rellena este campo</mat-error>
}
</mat-form-field>
</mat-card-content>
<mat-card-content>
<mat-form-field>
<mat-label>Correo electrónico</mat-label>
<input matInput [formControl]="emailFormControl" required name="email" maxlength="128" autocomplete="off">
@if (emailFormControl.hasError('required')) {
<mat-error>Por favor, rellena este campo</mat-error>
}
@else if (emailFormControl.hasError('email')) {
<mat-error>Debes introducir una dirección de correo electrónico válida</mat-error>
}
@else if (emailFormControl.hasError('pattern')) {
<mat-error>Debes introducir una dirección de correo electrónico válida</mat-error>
}
</mat-form-field>
</mat-card-content>
<mat-card-content>
<mat-form-field>
<mat-label>Mensaje</mat-label>
<textarea #contactBody matInput [formControl]="bodyFormControl" name="body" maxlength="1024" autocomplete="off"></textarea>
<mat-hint align="end">{{ contactBody.value.length }} / 1024</mat-hint>
@if (bodyFormControl.hasError('required') || bodyFormControl.hasError('pattern')) {
<mat-error>Por favor, rellena este campo</mat-error>
}
</mat-form-field>
</mat-card-content>
</form>
<button mat-raised-button color="primary" (click)="saveMessage()">Enviar formulario</button>
contact.component.ts:
import { Component, OnInit } from '@angular/core';
import { AbstractControl, ReactiveFormsModule, FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { AppService } from './../app.service';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ErrorStateMatcher } from '@angular/material/core';
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrl: './contact.component.scss'
})
export class ContactComponent implements ErrorStateMatcher {
contactForm: FormGroup = new FormGroup({
nameFormControl: new FormControl(''),
emailFormControl: new FormControl(''),
bodyFormControl: new FormControl(''),
})
constructor(private appService: AppService, private formBuilder: FormBuilder, private _snackBar: MatSnackBar) {}
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
}
ngOnInit(): void {
this.contactForm = this.formBuilder.group(
{
nameFormControl: [
'',
[
Validators.required,
Validators.pattern(/[\S]/)
]
], emailFormControl: [
'',
[
Validators.required,
Validators.email,
Validators.pattern("^([a-zA-Z0-9-._]+)@([A-Za-z-]+)\.([a-z]{2,3}(.[a-z]{2,3})?)$")
]
], bodyFormControl: [
'',
[
Validators.required,
Validators.pattern(/[\S]/g)
]
]
},
);
}
}
The issue is on the nameFormControl, emailFormControl and bodyFormControl, which are under the FormGroup contactForm.
Can you guys bring me some light to this subject?
Thank you very much!
FormGroup issue
You should not set [formGroup] on your form fields and [formControl] should be formControlName
<!-- Incorrect -->
<input matInput [formGroup]="contactForm" [formControl]="nameFormControl"
<!-- Correct -->
<input matInput formControlName="nameFormControl"
The @IF issue
You can't access the control directly You need to use the formGroup like this
@if (contactForm.controls.emailFormControl.hasError('required'))