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?

6
  • I made a detailed answer here stackoverflow.com/a/59285337/2398593 Commented Dec 12, 2019 at 10:41
  • hi @maxime1992 these are parent and child forms, in Two Different components Commented Dec 12, 2019 at 10:43
  • also, trying to do without third party libraries Commented Dec 12, 2019 at 10:45
  • "these are parent and child forms, in Two Different components" unsure what you're trying to achieve then sorry Commented Dec 12, 2019 at 10:48
  • @Artportraitdesign1 use same form for child as well [form]="form" angular.io/guide/dynamic-form Commented Dec 12, 2019 at 12:26

3 Answers 3

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

Sign up to request clarification or add additional context in comments.

5 Comments

and this same pattern will work with Multiple Child Component forms going to a single Parent Form?
@Artportraitdesign1 Yes, Please check updated link (I've updated the ans)
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?
@Mackelito 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.
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.. :)
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);
    }
}

Comments

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

Comments