Search code examples
angularangular-reactive-forms

form not able to show invalid field when using a nested component


I have a form that looks like this:

form = formGroup({

  name:FormControl(''),
  adress:new FormGroup({
    city: new FOrmControl(''),
  })

})

and I use two components

parent:

<form [formGroup]="form" (ngSubmit)="submit()">
  <input matInput formControlName="name">
  <child-component [group]="form.controls.adress"><child-component/>
</form>

child:

<form [formGroup]="group">
  <input matInput formControlName="city">
</form>

Now my problem is that when I submit the form and city is invalid, the red underline doesn't show. How can submit have an effect on both parent and child?


Solution

  • Please try the below code:

    parent.component.html

    <form [formGroup]="form" (ngSubmit)="submit()">
      <mat-form-field>
        <input matInput formControlName="name" placeholder="Name">
        <mat-error *ngIf="form.controls.name.invalid && (form.controls.name.dirty || form.controls.name.touched)">
          Name is required
        </mat-error>
      </mat-form-field>
    
      <app-child [group]="addressGroup"></app-child>
    
      <button type="submit">Submit</button>
    </form>
    

    parent.component.ts

    import { OnInit, Component } from '@angular/core';
    import { ApiResponse } from '../models/apiResponse';
    import { ChaptersViewModel } from '../models/chapters.model';
    import { HomeService } from './home.service';
    import { FormGroup, FormControl, Validators } from '@angular/forms';
    
    @Component({
      selector: 'app-parent',
      templateUrl: './parent.component.html',
      styleUrls: ['./parent.component.scss']
    })
    
    export class ParentComponent implements OnInit {
      form: FormGroup;
    
      ngOnInit() {
        this.form = new FormGroup({
          name: new FormControl('', Validators.required),
          address: new FormGroup({
            city: new FormControl('', Validators.required),
          }),
        });
      }
    
      submit() {
        this.markAllAsTouched(this.form);
        if (this.form.valid) {
          // Handle valid form submission
        }
      }
    
      markAllAsTouched(group: FormGroup) {
        Object.keys(group.controls).forEach((key) => {
          const control = group.get(key);
          if (control instanceof FormControl) {
            control.markAsTouched();
          } else if (control instanceof FormGroup) {
            this.markAllAsTouched(control);
          }
        });
      }
    
      get addressGroup(): FormGroup {
        return this.form.get('address') as FormGroup;
      }
    }
    

    child.component.html

    <form [formGroup]="group">
      <mat-form-field>
        <input matInput formControlName="city" placeholder="City">
        <mat-error *ngIf="group.controls.city.invalid && (group.controls.city.dirty || group.controls.city.touched)">
          City is required
        </mat-error>
      </mat-form-field>
    </form>
    

    child.component.ts

    import { Component, Input } from '@angular/core';
    import { FormGroup } from '@angular/forms';    
    
    @Component({
      selector: 'app-child',
      templateUrl: './child.component.html',
      styleUrls: ['./child.component.scss']
    })
    
    export class ChildComponent {
      @Input() group!: FormGroup;
      constructor() { }  
    }