Pass Angular Reactive FormControls to Children Components

JordanBarber picture JordanBarber · Jun 16, 2018 · Viewed 15.2k times · Source

I have a parent component where I want to create and store my Reactive Form. There will be multiple Form Group instances in a Form Array. I want to pass the Form Controls from each Form Group to a Child Component but am having trouble figuring out how to do this.

Here's a Stackblitz demonstrating what I would like to do. Also, some fields will only apply to certain 'makes' of cars which is why i have the following line in my html:

<input type="text" *ngIf="car.make != 'ford'" formControlName="model">

I would like to move the 'Details' Form Group fields into the 'details-fields' child component but when I try to do that, it tells me that I don't have a Form Group instance so I know the parent Form Group is not registering in the child component. Any help is much appreciated.

Answer

R. Richards picture R. Richards · Jun 16, 2018

It's not too tough, once you know what to look for. If you have never done it before, it can be a challenge. The error messages you get don't always help.

First, you want to add some things to your DetailsFields component. The new Inputs will allow you to pass in the instance of the form group you constructed in your parent component. The car and carIndex inputs will be used to pass in the values the child component will need in order to work like you have them now.

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

@Component({
    selector: 'app-child',
    templateUrl: './details-fields.component.html',
    styleUrls: ['./details-fields.component.css']
})

export class DetailsFields implements OnInit {
    @Input() form: FormGroup;
    @Input() car: any;
    @Input() carIndex: number;

    ngOnInit() { }
}

Second, you want to move some of the template HTML you have in you parent component to the child component. The child component template (details-fields.component.html) will look like this in the end. The [formGroup]="form" in the first div is the real key here. That is what tells the child template what form group to use. You pass this from the parent in the code after this.

<div [formGroup]="form">
    <div class="car-wrap" formArrayName="cars">
        <div [ngClass]="car.make + '-car'" [formGroupName]="carIndex">
            <p class="title">This car is a {{car.make}}</p>
            <div formGroupName="details">
              <input type="text" formControlName="make">
              <input type="text" *ngIf="car.make != 'ford'" formControlName="model">
              <input type="number" formControlName="year">
            </div>
            <div formGroupName="appearance">
              <input type="text" formControlName="color">
            </div>
        </div>
    </div>
</div>

The parent component template will be only left with this. This is where you pass the carsForm form group to the child component, along with the car and the carIndex.

<div id="cars" [formGroup]="carsForm">
    <div *ngFor="let car of cars; let i = index;">
        <app-child [form]="carsForm" [car]="car" [carIndex]="i"></app-child>
    </div>
</div>

The last thing I did was move the styles you had in the parent component out to the styles.css file, so all components could use them.

That's it! Really just moving some code around and adding some inputs so you can pass the child what it needs.