How to use formControlName and deal with nested formGroup?

As Angular documentation says we can use formControlName in our forms:

<h2>Hero Detail</h2>
<h3><i>FormControl in a FormGroup</i></h3>
<form [formGroup]="heroForm" novalidate>
    <div class="form-group">
        <label class="center-block">Name:
            <input class="form-control" formControlName="name">
        </label>
    </div>
</form>

As they say...

Without a parent FormGroup, [formControl]="name" worked earlier because that directive can stand alone, that is, it works without being in a FormGroup. With a parent FormGroup, the name input needs the syntax formControlName=name in order to be associated with the correct FormControl in the class. This syntax tells Angular to look for the parent FormGroup, in this case heroForm, and then inside that group to look for a FormControl called name.

Anyway some months ago I asked this to figure out what is the difference between formControlName and [formControl].

Now my question is: what about use formControlName with nested FormGroups?

For example, if I have the following form structure:

this.myForm = fb.group({
    'fullname': ['', Validators.required],
    'gender': [],
    'address': fb.group({
        'street': [''],
        'houseNumber': [''],
        'postalCode': ['']
    })
});

What is the right way to bind "street" (or "houseNumber" or "postalCode") to related HTML elements using formControlName?


Solution 1:

you can use Form group which is basically a collection of controls ( controls mean the fields given in your html form) define in your typescript syntax and binded to your HTML elements using the formControlName directive ,for example

this.myForm = fb.group({
    'fullname': ['', Validators.required],
    'gender': [],
    'address': fb.group({
        'street': [''],
        'houseNumber': [''],
        'postalCode': ['']
    })
});

Template:

<form [formGroup]="myForm" >
   <div class="form-group">
      <label for="fullname">Username</label>
      <input type="text" id="username" formControlName="fullname" class="form-control">            
   </div>
   <div class="radio" *ngFor="let gender of genders">
      <label>
      <input type="radio" formControlName="gender" [value]="gender">{{ gender }} </label>
   </div>
   <div formGroupName="address">
      <div class="form-group">
         <label for="street">Username</label>
         <input type="text" id="username" value="street" formControlName="street" class="form-control">            
      </div>
      <div class="form-group">
         <label for="houseNumber">Username</label>
         <input type="text" id="username" value="street" formControlName="houseNumber" class="form-control">            
      </div>
      <div class="form-group">
         <label for="postalCode">Username</label>
         <input type="text" id="username" value="street" formControlName="postalCode" class="form-control">            
      </div>
   </div>
</form>

A formGroup can consist of a nested formGroup and hierarchy can continue on ,but in accessing the value the its fairly simple .

Solution 2:

It is true. Look at formGroupName

this.myForm = fb.group({
    'fullname': ['', Validators.required],
    'gender': [],
    'address': fb.group({
        'street': [''],
        'houseNumber': [''],
        'postalCode': ['']
    })
});

<form [formGroup]="myForm" >
   <div class="form-group">
      <label for="fullname">Username</label>
      <input type="text" id="username" formControlName="fullname" class="form-control">            
   </div>
   <div class="radio" *ngFor="let gender of genders">
      <label>
      <input type="radio" formControlName="gender" [value]="gender">{{ gender }} </label>
   </div>
   <div formGroupName="address">
      <div class="form-group">
         <label for="street">Username</label>
         <input type="text" id="username" value="street" formControlName="street" class="form-control">            
      </div>
      <div class="form-group">
         <label for="houseNumber">Username</label>
         <input type="text" id="username" value="street" formControlName="houseNumber" class="form-control">            
      </div>
      <div class="form-group">
         <label for="postalCode">Username</label>
         <input type="text" id="username" value="street" formControlName="postalCode" class="form-control">            
      </div>
   </div>
</form>

Solution 3:

tl;dr:

You can break your form up into components that use your nested formgroups, and formcontrolname can be used as normal.

The approach I tend to use, since usually nested Formgroups are used to designate separate parts of a form, is to break it up into components and pass those components the nested formgroup as an input parameter. So in your case, I would have an address component that takes a formgroup as a parameter:

<app-address [formGroup]="myForm.get('address')"></app-address

And inside of that component, I would just have an @Input() formGroup that would be used in the html:

<div [formGroup]="formGroup">
....

That way you can reference the control name explicitly as you normally would, since it would be part of this formgroup.

Also, keep in mind the form is passed as reference. your changes would be accounted for in the parent component's myForm element, and if you needed access to parts of the form not in you formgroup (validation, change detection, ect ect) you could always pass down the whole form and just define the formgroup to reference the inner group explicitly:

<div [formGroup]="formGroup.get('adress')">

(assuming you pass down the entire form object that is

Happy coding!