Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular: Validate form with multiple child components

Tags:

angular

Using Angular 6 here:

I have a parent component and within that I have 3 child components. The child 1 component has a text fields, child 2 component has a dropdown and child 3 component has a dropdown and a submit button.

On the submit click button of child 3, I want to validate all the inputs of child1, child2 and child3 as they are required and throw error accordingly.

With angularjs I can just bind to ngmodel and check if form is invalid. How can I do so in angular? I mean how can I get and pass the state of input from one child to another etc. When searching I found the concept of reactive forms but most of the articles are of parents forms having input only.

Would appreciate if anyone can provide help on this. Below is the code I have

--Updated--

I updated below code after following this post: https://medium.com/@joshblf/using-child-components-in-angular-forms-d44e60036664

But this one keeps on giving me error in console: "Error: formControlName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class)."

--Updated code with reactive form--

--Parent--

<form class="form-horizontal" [formGroup]="myForm">
    <div class="row">     
      <ct-child1  [myForm]="myForm"></ct-child1>          
      <ct-child2> </ct-child2>      
    </div>
    <div class="row">      
      <ct-child3></ct-child3>         
    </div>   
</form>

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'ct-parent',
  templateUrl: './parent.component.html']
})
export class ParentComponent implements OnInit  {
  myForm: FormGroup;
  constructor(private fb: FormBuilder) { }
  ngOnInit() {
      this.myForm = this.fb.group({
      uname: ['', Validators.required]
    });
    }  
 }

 --Child 1--

<div class="panel-body">
  <div class="form-group">
    <label for="myForm" class="col-sm-3 control-label">Name:</label>
    <div class="col-sm-8">
      <input type="text" class="form-control" name="uname" [formControlName]="'uname'"  placeholder="Enter Name..." required >
    </div>
  </div>      
</div>

import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'ct-child1',
  templateUrl: './child1.component.html']
})
export class Child1Component implements OnInit { 
  @Input() myForm: FormGroup;
  constructor() { }
  uname: string; 

  ngOnInit() { }
}
like image 257
Brenda Avatar asked Oct 17 '22 05:10

Brenda


1 Answers

Build the form in your parent, pass whatever fields, or nested formgroups you want to the children. Parent will be aware of anything that goes on in the children. It knows if a specific form control is valid or not, is the whole form valid... all that it needs to know about the form!

Build the form in your parent, pass whatever fields you want. If you do not have nested formgroups that are passed to children, you need to pass the whole parentform to the child(ren), because all fields that are shown in the child components need to be wrapped inside a formgroup. What we are seing in your example is that you want to pass the whole form as @Input to the child(ren) just like you have. Then in your child, wrap the fields within the formgroup, just like a "regular" form:

<div [formGroup]="myForm">
  <label>Name:</label>
  <input type="text" formControlName="uname"  placeholder="Enter Name...">
</div>      

Also remove the brackets from formControlName, since you are not trying to use a variable binding, your formcontrol is simply named uname. Also you don't need anything like marking required in the template, since this is handled by the form when you set Validators.required (or whatever validator you want).

Showing validation errors you can use with calling hasError(), so in this sample myForm.hasError('required', 'uname') can be set with *ngIf.

And as I mentioned, have the submit button in the parent, since the parent controls the whole form. I guess you can have the button in child 3, but I see no point in making it overcomplicated and needing go up in the tree to get a hold of the parent form, when it's easily done having it in the parent, and that is also how it's recommended to do:

<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <ct-child1 [myForm]="myForm"></ct-child1>
  <button type="submit">Submit</button>
</form>

Here's a sample for you. Just having one child, but you can have as many different childs you like, just follow the same pattern as we did with child 1 :)

like image 110
AT82 Avatar answered Oct 19 '22 01:10

AT82