DEV Community

Muhammad Muhktar Musa
Muhammad Muhktar Musa

Posted on

Reactive Forms in Angular The basics

Introduction

Angular uses reactive methods to process and manage form. It is easy to use reactive forms to capture user inputs events, validate the inputs and create form models in large angular applications. This enables the tracking of data and changes in the form model in all parts of the application.

Reactive Form model setup

Reactive forms provide a model-driven approach to handling form inputs whose values change over time. It uses an explicit and immutable approach to manage the form at a given point in time and it is built around observable streams.

Adding a basic form control

  • Register the reactive form module in the app module declaration of an angular app. This module declares a reactive form directives needed to use the reactive form
  • Generate a new form control instance and save in the component class
  • Register the form control in the template.

Let us take a look at how to implement the above. To use the reactive form controls we need to import ReactiveFormsModule from @angular/forms package and add it to the NgModule imports array

import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
imports:[
ReactiveFormsModule],
  });
Enter fullscreen mode Exit fullscreen mode

Next is to generate a form control. To register a single form control, we import the form control class and create a new instance of FormControl that is saved as a class property.

import { FormControl } from '@angular/forms';

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

export class CartComponent implements OnInit {
  name = new FormControl('');
}
Enter fullscreen mode Exit fullscreen mode

By creating this control in the component class, immediate access is gotten to listen for, update and validate the state of the form input.
To register the control in the template, we can add a label to the template

<label>Name:
<input type="text" [formControl]="name">
</label>
Enter fullscreen mode Exit fullscreen mode

Displaying a form control value can be achieved through value changes from observables where changes are listened for in the template using AsyncPipe or in the component class using a subscribe method. It can also be achieved with the value property which gives a snapshot of the current value.
Let us take an example of how to display the value using interpolation in the template.

<label>Name:
<input type="text" [formControl]="name">
<p>
    value: {{name.value}}
</p>
</label>
Enter fullscreen mode Exit fullscreen mode

The displayed value changes as the form control element is updated. Reactive forms have methods they use to change a control value programmatically. This gives flexibility to update the value without user interaction. A form control instance provides a setValue() method that updates the value of the form control and validates the structure of the value provided against the control structure. To update the name we can use the setValue method as below

 upDateName() {
    this.name.setValue('Kings');
  }
Enter fullscreen mode Exit fullscreen mode

Update the template with a button to simulate name update

<label>Name:
<input type="text" [formControl]="name">
<p>
    value: {{name.value}}
</p>
</label>
<button (click)="upDateName()">Update Name</button>
Enter fullscreen mode Exit fullscreen mode

The form model is the source of truth for the control. When the button is clicked the value of the input is changed within the component class overriding its current value.

Grouping form controls

Forms typically contain several related controls. Reactive forms provide two ways of grouping multiple related controls into a single input form.

  • A form group that defines a dynamic form with a fixed set of controls that can be managed together.
  • A form array that defines a dynamic form where controls can be added or removed at run time. A single form control instance gives control over a single input field while a form group instance tracks the form state of a group of form control instances. let us take a look at this in play. Import the form group class from the angular packages
import { FormControl, FormGroup } from '@angular/forms';
Enter fullscreen mode Exit fullscreen mode

Create a form group instance, associate the form group model and view then save the data.

profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastname: new FormControl('')
  });
Enter fullscreen mode Exit fullscreen mode

Associate the form group model and view in the template

<form [formGroup]="profileForm ">
    <label>First Name:
        <input type="text" formControlName="firstName">
        <p>
            value: {{name.value}}
        </p>
        </label>
        <label>Last Name:
            <input type="text" formControlName="lastName">
            <p>
                value: {{name.value}}
            </p>
            </label>
</form>
Enter fullscreen mode Exit fullscreen mode

To save the form data the form group directive listens for the submit event emitted by the form element which can be bind to a callback function. let us add an ngSubmit event listener to the form tag with the onSubmit() callback method.

<form [formGroup]="profileForm " (ngSubmit)="onSubmit()">
    <label>First Name:
        <input type="text" formControlName="firstName">
        <p>
            value: {{name.value}}
        </p>
        </label>
        <label>Last Name:
            <input type="text" formControlName="lastName">
            <p>
                value: {{name.value}}
            </p>
            </label>
</form>
Enter fullscreen mode Exit fullscreen mode

add the method to the class

  onSubmit() {
console.log(this.profileForm.value);

  }
Enter fullscreen mode Exit fullscreen mode

Use a button element to add a button to the form to trigger the form submission

 <button type="submit" [disabled]="profileForm.valid"></button>
Enter fullscreen mode Exit fullscreen mode

Creating nested form groups

Form groups can accept individual form control instances and other form group instances as children. This makes composing complex form models easier to maintain and logically grouped together. Let us create and take a look at a complex form.

 profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastname: new FormControl(''),
    address: new FormGroup({
      street: new FormControl(''),
      city: new FormControl(''),
      state: new FormControl(''),
      zip: new FormControl('')
    })
  });
Enter fullscreen mode Exit fullscreen mode

Group the nested form in a template

<div formGroupName="address">
   <h5>Address</h5>
        <label>City:
            <input type="text" formControlName="city">
            <p>
                value: {{city.value}}
            </p>
        </label>
        <label>Street Name:
            <input type="text" formControlName="street">
            <p>
                value: {{street.value}}
            </p>
        </label>
        <label>State Name:
            <input type="text" formControlName="state">
            <p>
                value: {{state.value}}
            </p>
        </label>
        <label>Zip:
            <input type="text" formControlName="zip">
            <p>
                value: {{zip.value}}
            </p>
        </label>
    </div>
Enter fullscreen mode Exit fullscreen mode

The updateProfile() method can be used to update the firstName and street of the user

updateProfile() {
    this.profileForm.patchValue({
      firstName: 'jules',
      address: {
        street: '234 jules miles street'
      }
    })
  }
Enter fullscreen mode Exit fullscreen mode

simulate an update by adding a button to the user profile

<button (click)="updateProfile()">update profile</button>
Enter fullscreen mode Exit fullscreen mode

Generating controls using the formbuilder service

To generate controls using the formbuilder service, we need to import the formbuilder class then inject the formbuilder service and then generate the form contents.

import the formbuilder class

import { FormBuilder} from '@angular/forms';
Enter fullscreen mode Exit fullscreen mode

inject the formbuilder service

constructor( private fb: FormBuilder) { }
Enter fullscreen mode Exit fullscreen mode

generate the controls

profileForm = this.fb.group({
      id: [''],
      teacherIds: [''],
      studentIds: [''],
    });
Enter fullscreen mode Exit fullscreen mode

Validating form input

Form validation is used to ensure that user input is complete and correct. To achieve this we import a validator function in the form component

import { Validators} from '@angular/forms'
Enter fullscreen mode Exit fullscreen mode

Add logic and validators to the form fields required

profileForm = this.fb.group({
      id: ['', validators.required],
      teacherIds: [''],
      studentIds: [''],
    });
Enter fullscreen mode Exit fullscreen mode

Add the validator to the template

<input placeholder="id" formControlName="id" required />
Enter fullscreen mode Exit fullscreen mode

Display current status of form by using interpolation

<p> Form status: {{profileForm.status}} </p>
Enter fullscreen mode Exit fullscreen mode

We can see from our discussion that building a form using the reactive form approach makes managing immutable form input data at a given point much easy.

Discussion (0)