zer0
zer0

Reputation: 5017

Validate child component form when a parent component button is clicked in Angular

I have two buttons in the parent template. One button acts as the Submit for the parent/child form. The other should validate the child component form. Clicking the Submit button validates both - which is OK. However, I am not able to get the other button to validate my child component form. Essentially, show the required errors of Child form only when the "Check Phone" button is clicked.

Parent template:

    <form id="sampleform" [formGroup]="sampleForm" (ngSubmit)="formsubmit()">
  <mat-form-field>
    <input matInput type="text" formControlName="name" [errorStateMatcher]="matcher">
    <mat-error *ngIf="sampleForm.controls.name.hasError('required') && isShowErrors">This field is invalid</mat-error>
  </mat-form-field>
  <br/>
  <br/>
  <child-component [isShowErrors]="isShowErrors"></child-component>
</form>

<button type="button">Check phone</button> <br/>
<button type="submit" form="sampleForm" (click)="formsubmit()">Submit</button>

Parent Component TS:

export class AppComponent{
  isShowErrors: boolean = false;
  sampleForm: FormGroup;
  matcher = new MyErrorStateMatcher();
  constructor(private fb: FormBuilder){

  }
  ngOnInit(){
    console.log(">>>>")
    this.sampleForm = this.fb.group({
      name: ['', Validators.required]
    });
  }

  formsubmit(){
    this.isShowErrors = true;
    console.log(this.sampleForm);
    if(this.sampleForm.valid){
      //navigate
    }
  }
}

Child Component template:

<form [formGroup]="sampleChildForm">
  <mat-form-field>
    <input matInput type="text" formControlName="phone" [errorStateMatcher]="matcher">
    <mat-error *ngIf="sampleChildForm.controls.phone.hasError('required') && isShowErrors">Test Error</mat-error>
</mat-form-field>
</form>

Stackblitz: https://stackblitz.com/edit/angular-gyzaag

Upvotes: 5

Views: 8147

Answers (1)

danday74
danday74

Reputation: 56936

Here's a full working blitz ... https://stackblitz.com/edit/angular-7nbjnh ... that implements the solution given herein.

In child component controller add this method:

formsubmit(){
  this.isShowErrors = true;
  console.log(this.sampleChildForm);
  if(this.sampleChildForm.valid){
    //navigate
  }
}

In parent component html change these lines:

<child-component #fred [isShowErrors]="isShowErrors"></child-component>

<button type="button" (click)="checkPhone()">Check phone</button>

In parent controller do this:

@ViewChild('fred') fred;

checkPhone() {
  this.fred.formsubmit()
}

This basically allows the parent to call a method in the child. The key here is the #fred reference variable. When you put a reference variable on an angular component it gives you access to the component class and its methods, so you can easily call child.formsubmit() AKA fred.formsubmit() from the parent.

Not relevant here but worthing knowing, when you put a reference variable on an HTML element it gives you access to the HTML element (in the same way that getElementById does).

If interested, there is a newer slight variation of this approach which still uses @ViewChild but does not require a reference variable here:

https://angular.io/guide/component-interaction#parent-calls-an-viewchild

This alternative approach is principally identical in that the parent still calls a method in the child. The hookup is just minimally different.

If you wish to use the alternative just drop the #fred reference variable and do this for @ViewChild in the parent controller:

import { ChildComponent } from './child/child.component';
@ViewChild(ChildComponent) private fred: ChildComponent;

I personally prefer use of the reference variable since it seems less tightly bound but I believe the latter approach is considered best practice since it is in the Angular docs under Component Interaction.

Upvotes: 3

Related Questions