Angular Use Child Component Form in Parent Component Nested Form
Asked Answered
M

3

6

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?

Mikaelamikal answered 12/12, 2019 at 10:22 Comment(6)
I made a detailed answer here https://mcmap.net/q/1772580/-angular-how-to-access-the-value-of-controls-in-nested-formsGentility
hi @Gentility these are parent and child forms, in Two Different componentsMikaelamikal
also, trying to do without third party librariesMikaelamikal
"these are parent and child forms, in Two Different components" unsure what you're trying to achieve then sorryGentility
@Artportraitdesign1 use same form for child as well [form]="form" angular.io/guide/dynamic-formSagittate
hi @Sagittate feel free to write as answer and I can send points, I guess thats the Angular official answer guide?Mikaelamikal
S
8

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

Sagittate answered 12/12, 2019 at 17:55 Comment(5)
and this same pattern will work with Multiple Child Component forms going to a single Parent Form?Mikaelamikal
@Artportraitdesign1 Yes, Please check updated link (I've updated the ans)Sagittate
What if you need to nest it even deeper? let's say that you have a child component for postalCode but you still want to keep it in the parent components FormGroup? (and not specify the parent FormGroup eg if I add the postalCode component to both the app.component and the secondChild.component?Samoyed
@Samoyed If I have getting your point right, than you need to create group for child and put the value what you want in that scenario parent and child is separate.Sagittate
I solved it by using the NG_VALUE_ACCESSOR.. so now each component you just add the formControlName and it will "connetct" to the form.. :)Samoyed
R
2

After much discussion and trial and error, my team settled on something that is almost the first approach, i.e. the child component defines the subform and the corresponding form group. But we don't set the form control using @ViewChild() and AfterViewInit. Instead we pass the parent form down to the child component and let the child set up the link. This is because we have one child component that is used in potentially many different parents and repeating the AfterViewInit snippet gets old fast. Additionally this approach also works if your parent form is wrapped in an *ngIf-directive as the wiring happens when the child component gets initialized and not before (no more pesky @ViewChild() set childComponent() {} shenanigans necessary).

<!-- parent form -->
<app-child-form [parent]="form"></app-child-form>
// Child component
@Component({
    selector: 'app-child-form',
    templateUrl: './child-form.component.html',
})
export class ChildComponent {
    @Input()
    parent!: FormGroup;

    form: FormGroup;

    constructor(private fb: FormBuilder) {
        this.form = this.fb.group({
            // ...
        });
    }

    ngOnInit() {
        this.parent.addControl('child-form', this.form);
        this.form.setParent(this.parent);
    }
}
Rodas answered 24/1, 2022 at 10:30 Comment(0)
T
0

Just use this little gem in the child component, nothing else is needed:

@Component({
  ... normal stuff,
  viewProviders: [{ provide: ControlContainer, useExisting: NgForm }]
})

works for template-driven forms. I believe it works for reactive forms too but I haven't tested that

Terce answered 23/4 at 22:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.