Angular Forms Several Ways: Reactive, Nested, Across Routes
Tags: angular, forms, javascript
Published: 2019-10-11 23:45:11

---
title: Angular Forms Several Ways: Reactive, Nested, Across Routes
published: true
description: How to create manageable Angular reactive forms in a variety of situations: creating nested and re-usable form components and forms across multiple routes using the ControlContainer class. 
tags: Angular, Forms, JavaScript
canonical_url: https://jenniferwadella.com/blog/angular-control-container
---


Dealing with forms is rarely simple anymore. Many SaaS products offer highly customizable setups managed by complex and dynamic forms. Knowing how to use Angulars [ControlContainer](https://angular.io/api/forms/ControlContainer) will give you more control, heh, over managing your forms.

## ControlContainer

The ControlContainer is a base class for form directives that contain multiple registered instances of NgControl. We can use the ControlContainer to access FormControls, FormGroups, and FormArrays and manage a main form chunked across components.

A common situation is having a group of form controls, like an "address" groups of fields like "street", "city", "zip" that  you use repeatedly across your application. To use bind a Reactive FormControl to the DOM we need access to that FormControl - and can use the FormGroup directive to pass a main FormGroup instance to nested components.

```typescript
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-span-form',
  template: `
  <form [formGroup]="sampleForm">
    <div class="form-group">
      <label for="name">First Name</label>
      <input name="first_name" formControlName="first_name" />
    </div>
    <div class="form-group">
      <label for="name">Last Name</label>
      <input name="last_name" formControlName="last_name" />
    </div>
    <div class="form-group">
      <label for="name">Email Address</label>
      <input name="email" formControlName="email" />
    </div>
    <app-address></app-address>
  </form>
  `,
  styleUrls: ['./span-form.component.less']
})
export class SpanFormComponent implements OnInit {
  public sampleForm: FormGroup;
  constructor(
    private fb: FormBuilder
  ) { }

  ngOnInit() {
    this.sampleForm = this.fb.group({
      user_name: ['', Validators.required],
      first_name: ['',Validators.required],
      last_name: ['',Validators.required],
      email: ['',Validators.required],
      street: ['',Validators.required],
      city: ['',Validators.required],
      state: ['',Validators.required],
      zip: ['',Validators.required]
    })
  }

}
```

Inside our reusable Address component we can access the `sampleForm` by injecting the ControlContainer class in our constructor - this will return the parent FormGroupDirective and allow us to access that control. From there we can use the `formControlName` for our FormControls as expected.

```typescript
import { Component, OnInit } from '@angular/core';
import { ControlContainer } from '@angular/forms';

@Component({
  selector: 'app-address',
  template: `
  <form  *ngIf="ogFormGroup" [formGroup]="ogFormGroup">
    <h5>Address:</h5>
    <div class="form-group">
      <label for="name">Street Name</label>
      <input formControlName="street" />
    </div>
    <div class="form-group">
      <label for="name">City</label>
      <input formControlName="city" />
    </div>
    <div class="form-group">
      <label for="name">State</label>
      <input formControlName="state" />
    </div>
    <div class="form-group">
      <label for="name">Zip</label>
      <input formControlName="zip" />
    </div>
  </form>
  `,
  styleUrls: ['./address.component.less']
})
export class AddressComponent implements OnInit {
  public ogFormGroup;
  constructor(public controlContainer: ControlContainer) {
  }

  ngOnInit() {
    this.ogFormGroup = this.controlContainer.control;
  }

}
```

We can use the same approach in other situations, for example a form that's presented across multiple paths.

```typescript
//parent component where form is initialized
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-span-form',
  template: `
    <button routerLink="step-1">Step 1</button>
    <button routerLink="step-2">Step 2</button>
    <button routerLink="step-3">Step 3</button>
    <form [formGroup]="mainForm" class="form-ui">
      <router-outlet></router-outlet>
    </form>
  `,
  styleUrls: ['./span-form.component.less']
})
export class SpanFormComponent implements OnInit {
  public mainForm: FormGroup;
  constructor(
    private fb: FormBuilder
  ) { }

  ngOnInit() {
    this.mainForm = this.fb.group({
      user_name: ['', Validators.required],
      first_name: ['',Validators.required],
      last_name: ['',Validators.required],
      email: ['',Validators.required],
      address: this.fb.group({
        street: ['',Validators.required],
        city: ['',Validators.required],
        state: ['',Validators.required],
        zip: ['',Validators.required]
      }),
      favorite_color: ['',Validators.required],
      favorite_food: ['',Validators.required],
      favorite_season: ['',Validators.required],
      favorite_episode: ['',Validators.required]
    })
  }

}
```

In the child component we access the parent formDirective through the `ControlContainer` class and bind it to a new form directive in our component and include the formControls we want to present to the user.

```typescript
// child component at nested route displaying part of main form:
import { Component, OnInit } from '@angular/core';
import { ControlContainer } from '@angular/forms';

@Component({
  selector: 'app-step1',
  template: `
    <h3>Step One of Our Sign-in Process</h3>
    <form [formGroup]="parentForm">
      <div class="form-group">
        <label for="name">Choose a User Name</label>
        <input name="user_name" formControlName="user_name" />
      </div>
    </form>
    <button [disabled]="!parentForm.controls.user_name.valid"
    routerLink="/signup/step-2">Next</button>
  `,
  styleUrls: ['./step1.component.less']
})
export class Step1Component implements OnInit {
  public parentForm;
  constructor(private controlContainer: ControlContainer) {
  }

  ngOnInit() {
    this.parentForm = this.controlContainer.control;
  }
}
```

Code demo here: [https://github.com/tehfedaykin/ControlContainerExample](https://github.com/tehfedaykin/ControlContainerExample)

Cheers from a dairy farm 🐮in New Zealand!