skip to Main Content

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!

3

Answers


  1. 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'))
    
    Login or Signup to reply.
  2. You should use formControlName if you are using it inside FormGroup, and don’t add [formGroup] on input fields. like this:

     <input matInput formControlName="nameFormControl" required name="name" maxlength="128" autocomplete="off" autofocus>
    

    Remove [formGroup] in the input fields and just replace the followings:

    [formControl]="nameFormControl"
    

    With this :

    formControlName="nameFormControl"
    

    Also you cannot access the control directly without the contactForm object.
    So you need to replace following:

    @if (emailFormControl.hasError('required'))
    

    To:

    @if (contactForm.controls['emailFormControl'].hasError('required'))
    

    Tip:
    If you are using Visual Studio Code, then I would highly recommend using Angular Language Service Extension. It will show you the errors in the html file and help you fix them.

    Also when you are using FormGroup, use can consider controls as the fields, so you can simply name them like this:

    nameFormControl to name
    emailFormControl to email
    bodyFormControl to body

    Like this:

      contactForm: FormGroup = new FormGroup({
        name: new FormControl(''),
        email: new FormControl(''),
        body: new FormControl(''),
      })
    
    Login or Signup to reply.
  3. Couple things you need to correct in your template :

    1. Change your [formControl]="nameFormControl" to formControlName="nameFormControl" and follow the same to other controls as well

    2. Remove [formGroup]="contactForm" from nameFormControl

    3. Replace @if (emailFormControl.hasError('required')) from @if (contactForm.controls['emailFormControl'].hasError('required'))

    Please find the corrected code below :

    <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
              formControlName="nameFormControl"
              required
              name="name"
              maxlength="128"
              autocomplete="off"
              autofocus
            />
          </mat-form-field>
        </mat-card-content>
        <mat-card-content>
          <mat-form-field>
            <mat-label>Correo electrónico</mat-label>
            <input
              matInput
              formControlName="emailFormControl"
              required
              name="email"
              maxlength="128"
              autocomplete="off"
            />
          </mat-form-field>
        </mat-card-content>
        <mat-card-content>
          <mat-form-field>
            <mat-label>Mensaje</mat-label>
            <textarea
              #contactBody
              matInput
              formControlName="bodyFormControl"
              name="body"
              maxlength="1024"
              autocomplete="off"
            ></textarea>
            <mat-hint>{{ contactBody.value.length }} / 1024</mat-hint>
          </mat-form-field>
        </mat-card-content>
      </form>
      <button mat-raised-button color="primary" (click)="saveMessage()">
        Enviar formulario
      </button></mat-card
    >
    

    Please refer the working stackblitz here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search