Angular Reactive Forms with ng-bootstrap

The Angular + Bootstrap is still my favorite front end development stack. I love Angular because of the clear separation of the view and controller. It’s probably because of my heavy career background in MVC frameworks such as Struts and Spring MVC, back when things weren’t all in the browser. Angular is also heavily focused on Typescript, which has much stronger typing than pure JS. Bootstrap is also just one of those tools that I find very easy to use, especially because of it’s focus on responsive design. I can easily put together useful UIs quickly which work across most browsers and screens.

The ng-bootstrap project helps because it brings Angular widgets built from the ground up using only Bootstrap 4 CSS with APIs designed for the Angular ecosystem. There are no dependencies on 3rd party JavaScript.

Login Form Example

For this code example, I am building the login form for an example application.

Login Form after clicking the submit button

The controller looks like this.

import {Component, OnInit} from '@angular/core';
import {AbstractControl, FormBuilder, Validators} from "@angular/forms";

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {

  loginForm = this.formBuilder.group({
    email: ['', [Validators.email, Validators.required]],
    password: ['', [Validators.required]]
  })

  constructor(private formBuilder: FormBuilder) {
  }

  ngOnInit(): void {
  }

  onSubmit() {
    if (this.loginForm.invalid) {
      return;
    }
    console.info("Form Valid!")
  }

  get controls(): { [p: string]: AbstractControl } {
    return this.loginForm.controls;
  }

}

Here’s the HTML for the form.

<h1>Login</h1>
<form #form="ngForm" [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="form">
  <div class="card mt-3">
    <div class="card-body">
      <div class="form-group">
        <label for="email">Email</label>
        <input id="email" type="email" formControlName="email" class="form-control"
               [ngClass]="{ 'is-invalid': form.submitted && controls.email.invalid }">
        <div *ngIf="form.submitted && controls.email.invalid" class="text-danger">
          <small *ngIf="controls.email.errors?.required">Email is required.</small>
          <small *ngIf="controls.email.errors?.email">Email is invalid.</small>
        </div>
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input id="password" type="password" formControlName="password" class="form-control"
               [ngClass]="{ 'is-invalid': form.submitted && controls.password.invalid }">
        <div *ngIf="form.submitted && controls.password.invalid" class="text-danger">
          <small *ngIf="controls.password.errors?.required">Password is required.</small>
        </div>
      </div>
      <div class="form-group">
        <button type="submit" class="btn btn-success">Login</button>
      </div>
    </div>
  </div>
</form>

Notes on Implementation

  1. You’ll notice the #form="ngForm tag on the form. That’s the NgForm directive and it’s there for one reason: checking the submission status on the form. You’ll see a bunch of other tutorials that all have a separate controller variable, like submitted, and then you have to code to set it to true during the onSubmit portion. By using the NgForm directive, we can simply refer to the submission status directly using form.submitted.
  2. I expose the form controls as a group, rather than each individually. This simplifies the controller code and gives us access in the view to all the status and errors associated with each form field.

Leave a Reply

Your email address will not be published. Required fields are marked *