16

How do i use formGroupName inside child components? for example:

i have ParentFormComponent

    parentForm: FormGroup;
    constructor(private fb: FormBuilder, private http: Http) { }
    ngOnInit() {


 this.parentForm = this.fb.group({
        _general: this.fb.group ({
           ProjectName:''
       })
  })
 }

In the html:

  <form [formGroup]="parentForm" (ngSubmit)="submitForm()">
     <div formGroupName="_general">
        <mat-form-field>
             <input matInput placeholder="Project name" 
             formControlName="ProjectName">
        </mat-form-field>
    </div> 
  </form> 

it's working great but when i want to use child component it's not working:

<form [formGroup]="parentForm" (ngSubmit)="submitForm()">
          <app-child [parentForm]='parentForm'></app-child>
      </form> 

when i insert it to the child component:

<div formGroupName="_general">
            <mat-form-field>
                 <input matInput placeholder="Project name" 
                 formControlName="ProjectName">
            </mat-form-field>
        </div> 

and in the ts file

 @Input() parentForm:FormGroup;

i"m getting error: formGroupName 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).

Livnat Menashe
  • 831
  • 2
  • 9
  • 17

2 Answers2

36

Instead of Using input property binding Use FormGroupDirective

FormGroupDirective

This directive accepts an existing FormGroup instance. It will then use this FormGroup instance to match any child FormControl, FormGroup, and FormArray instances to child FormControlName, FormGroupName, and FormArrayName directives.

Use Viewproviders to provide controlContainer, Inject FormGroupDirective in your child component to get the parentform instance

app.parent.html

<form [formGroup]="parentForm">
  <app-child></app-child>
</form>

child.component.ts

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, ControlContainer, FormGroupDirective, Validators, FormBuilder, NgModel } from '@angular/forms';
@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css'],
  viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
})
export class ChildComponent implements OnInit {
  childForm;
  constructor(private parentF: FormGroupDirective) { }

  ngOnInit() {

    this.childForm = this.parentF.form;
    this.childForm.addControl('_general', new FormGroup({
      ProjectName: new FormControl('')
    }))

  }
}

child.component.html

<div formGroupName="_general">
     <mat-form-field>
                 <input matInput placeholder="Project name" 
                 formControlName="ProjectName"> 
      <mat-form-field>  
 </div>

Example:https://stackblitz.com/edit/angular-ezqupz

Chellappan வ
  • 23,645
  • 3
  • 29
  • 60
  • 1
    Is there a way to add mutiple components with the same formGroupName ? – billyjov Dec 12 '19 at 12:43
  • @billyjov what do you mean by multiple components? – Chellappan வ Dec 12 '19 at 13:02
  • i'm trying something like this in the CLI https://stackblitz.com/edit/angular-form-magic – billyjov Dec 12 '19 at 13:38
  • It's works fine on stackblitz but not inside my app – billyjov Dec 12 '19 at 13:39
  • Works thanks... i forgetted to use new FormGroup to build the parent form – billyjov Dec 14 '19 at 15:47
  • why not just pass the formgroup as an input? `` or `` – Rusty Rob Mar 16 '20 at 06:12
  • 1
    This works when the field is directly inside the form group defined by `[formGroup]` but `formGroupName` doesn't work for me. I'd have to use `[formGroup]="parentForm.get('_general')"` in this example. The example on stackblitz doesn't seem to use nested form groups. Is there a way to get formGroupName to work too? – Claude Martin Apr 29 '20 at 09:14
  • Do You want to get nested form group name inside custom component? @ClaudeMartin – Chellappan வ Apr 29 '20 at 09:21
  • @Chellappanவ I just want to use `formGroupName="foo"` like I could when not using a custom component. I don't know why it doesn't work on my project. I get `Cannot find control with name: 'xxx'`. It works when I use `[formGroup]="root.get('foo')"` instead. Is there another directive I have to declare for it to work? – Claude Martin Apr 29 '20 at 09:29
  • If it possible can you please create stackblitz? – Chellappan வ Apr 29 '20 at 09:32
  • 1
    @Chellappanவ I forked yours: https://stackblitz.com/edit/angular-ynjdlm This doesn't work because I use `formGroupName`. See `app.component.html` to see how `[formGroup]` would work, but `formGroupName` doesn't. – Claude Martin Apr 29 '20 at 09:59
  • 1
    You could do something like this as a workaround: https://stackblitz.com/edit/angular-ssryuc. – Chellappan வ Apr 29 '20 at 10:27
  • If you are looking for more generic check this blog:https://medium.com/angular-in-depth/angular-nested-reactive-forms-using-cvas-b394ba2e5d0d – Chellappan வ Apr 29 '20 at 10:29
  • So I ended up implementing `ControlValueAccessor`. Then it works. – Claude Martin May 13 '20 at 07:04
  • 1
    How do you unit test components using this approach? How do you avoid `No provider for FormGroupDirective`? I've tried a number of approaches but none have worked. – Splaktar Aug 14 '20 at 06:44
  • @Splaktar Can you create sample repo or something? – Chellappan வ Aug 14 '20 at 15:24
2

You don't even need to pass the formGroup to the child component, just provide a ControlContainer and use formGroupName like this:

parent html:

<form [formGroup]="parentForm">
  <app-child></app-child>
</form>

parent ts:

 this.parentForm = this.fb.group({
        _general: this.fb.group ({
           ProjectName:''
       })
  })

child html:

<div formGroupName="_general">
     <mat-form-field>
                 <input matInput placeholder="Project name" 
                 formControlName="ProjectName"> 
      <mat-form-field>  
 </div>

child ts:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, ControlContainer, FormGroupDirective, Validators, FormBuilder, NgModel } from '@angular/forms';
@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css'],
  viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
})
export class ChildComponent implements OnInit {
  constructor() { }

  ngOnInit() {

  }
}

That way, your entire form configuration is in the parent component without splitting it into the childrens .ts, they only act as views.

Elazar Zadiki
  • 928
  • 10
  • 22