Search code examples
angulartypescriptangular-reactive-formsangular8

Angular Use Child Component Form in Parent Component Nested Form


Whats the best way to place a ChildComponent form into a Parent Component Form? We are using the latest Angular 8 in 2019. The following methods below after research do not work fully.

Parent Component:

 ngOnInit() {
    this.parentForm = this.fb.group({
       childForm1: etc
    })

Child Component:

this.ChildForm = this.formBuilder.group({
  'streetNumber': [null, [Validators.required, Validators.maxLength(32)]],
  'streetType': [null, [Validators.maxLength(8)]],
  'city': [null, [Validators.maxLength(32)]],
  'state': [null, [Validators.maxLength(16)]],
  'postalCode': [null, [Validators.maxLength(16)]],
}, { validator: atLeastOneLocationRequired })

}

Method 1:

This method, https://itnext.io/partial-reactive-form-with-angular-components-443ca06d8419 after rigorous testing states ParentForm is Valid, even if the Child Form is invalid. This should not occur.

ngOnInit() {
  this.parent = this.fb.group({
    fullName: null
  })

}

formInitialized(name: string, form: FormGroup) {
  this.checkoutForm.setControl(name, form);
}

Method 2:

Method 2 utilizes ViewChild, which are hearing is bad practice. https://davembush.github.io/attaching-an-angular-child-component-s-form-to-a-parent/

@ViewChild(ChildComponent) childComponent: ChildComponent;

And now in ngAfterViewInit() we can add the child’s FormGroup as an additional “control” and set the parent FormGroup to the parent control’s FormGroup.

ngAfterViewInit() {
  this.form.addControl('childForm', this.childComponent.form);
  this.childComponent.form.setParent(this.form);
}

So what is best Angular official practice in Angular 8?


Solution

  • I've create small scenario according to your problem.

    parentComponent:

    HTML:

    <form [formGroup]="form">
      <app-child [form]="form"></app-child>
      <pre>{{form.value | json}}</pre>
    </form>
    

    TS:

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormBuilder, Validators } from '@angular/forms';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent implements OnInit {
      form: FormGroup;
      constructor (private fb: FormBuilder) {}
    
       ngOnInit() {
        this.form = this.fb.group({
          childForm1: '',
          streetNumber: [null, [Validators.required, Validators.maxLength(32)]],
          streetType: [null, [Validators.maxLength(8)]],
          city: [null, [Validators.maxLength(32)]],
          state: [null, [Validators.maxLength(16)]],
          postalCode: [null, [Validators.maxLength(16)]],
        })
      }
    }
    

    childComponent:

    HTML:

    <div [formGroup]="form">
      <input formControlName="streetNumber"><br>
      <input formControlName="streetType"><br>
      <input formControlName="city"><br>
      <input formControlName="state"><br>
      <input formControlName="postalCode">
    </div>
    

    TS:

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

    Working link: https://stackblitz.com/edit/angular-gjrphg

    Updated link for multiple child: https://stackblitz.com/edit/angular-svnqfh?file=src/app/app.component.html