Skip to content

Lets have a quick overview of Angular's Reactive Model Driven Forms with live practical example.

Notifications You must be signed in to change notification settings

dinanathsj29/angular-forms-reactive-tutorial

Repository files navigation

Angular logo

Angular Reactive Forms (Model driven)

Working with existing/cloned/copied Angular App

  • Clone or Download the project/app from Github or any other sources
  • If using Visual Studio Code / Insiders, open Command panel/terminal from menu: View -> Terminal (shortcut key is CTRL + BackTick OR COMMAND + J)
  • Go inside the project/app directory, command: cd _examples-angular-templateDrivenForm OR cd templateDrivenForm
  • Run command: npm install to install project/app dependencies (node_modules)
  • To Build and run Angular App, command: ng serve / npm start OR ng serve -o OR ng serve --open
  • To change the port from 4200 to other port - type command: ng serve --port 5000
  • To check the application in browser type path/URL: localhost:4200 / 5000

1.1. Reactive Model Driven Forms

We know that:

  • Reactive forms give the ability to control validation from the component code
  • Also, we are able to do a unit test of reactive model-driven forms

1.2. Setup Angular Project

  1. Create a new Angular project with command at the console: ng new angular-reactive-forms-tutorial
  2. Change Directory and get inside the directory with the command: cd angular-reactive-forms-tutorial and check angular app folder structure

   

Angular project/app folder structure

    Image - Angular project/app folder structure

  1. Run/Serve/Build Angular application with the command: ng serve or ng serve -o or ng serve --open
    • -o or --open flag directly launch the application into default browser (default port is 4200)
    • One can also change the port number by using the command: ng serve --port 5000 -o
  2. Go to browser and type: http://localhost:5000 to launch an angular application

   

Angular default app launch

    Image - Angular default app launch

1.3. Constructing HTML Form structure

  1. In Index.html file, in head element, link/import/include any front-end UI framework like Bootstrap or Zurb Foundation which helps to easily style our app/form layout

Syntax & Example: index.html

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.3.1/css/foundation.min.css">

or

<!-- link/import/include zurb foundation -->
<link rel="stylesheet" href="./assets/library/foundation.min.css">
  1. To verify the Zurb Foundation included/working properly in an application, check the angular app in browser fonts, etc changed?
    • Right click on page/element and check in inspect element the Zurb Foundation classes and properties applied to respective elements:

   

Zurb Foundation framework styly applied

    Image - Zurb Foundation framework styly applied

  1. From file app.component.html delete all old code and add new markup to create form:

Syntax & Example: app.component.html

<!-- show this template if name property not exist or form not submitted -->
<div *ngIf="!name; else formsubmitdata">

  <form>

    <div class="form-container">

      <div class="row columns">
        
        <h1>Beginners Simple Reactive Form</h1>

        <!-- name -->
        <label>Name
          <input type="text" placeholder="Enter Name">
        </label>

        <!-- description -->
        <label>Description
          <textarea placeholder="Enter Description"></textarea>
        </label>

        <!-- checkbox -->
        <label for="validate">Minimum of 5 Characters Name required</label>
        <input type="checkbox" name="validate" value="1"> ON

        <!-- submit button -->
        <input type="submit" class="button expanded" value="Submit Form">

      </div>

    </div>

  </form>

</div>

<!-- show this template if name property exists or form submitted -->
<ng-template #formsubmitdata>

  <div class="form-container">

    <div class="row columns">

      <h1>You Entered Name: {{ name }}</h1>

      <p>Your Description Details: {{ description }}</p>

    </div>

  </div>
  
</ng-template>

   

Simple HTML Form with Zurb Foundation

    Image - Simple HTML Form with Zurb Foundation

1.4. Styling HTML Form with CSS

  • With the Zurb Foundation framework some styling applied, let us create some custom css style to make form/interface more attractive and intuative

Syntax & Example: styles.css

/* You can add global styles to this file, and also import other style files */

/* apply google font family */
@import url('https://fonts.googleapis.com/css?family=ZCOOL+XiaoWei');

body {
  background:rgba(148, 134, 93, 0.35);;
  /* font-family: 'ZCOOL XiaoWei', serif !important; */
}

* {
  font-family: 'ZCOOL XiaoWei', serif !important;
}

.form-container {
  display:block;
  width:90%;
  padding:2em;
  margin: 2em auto;
  background:#fff;
}

h1 {
  text-align: center;
  margin-bottom: 1rem;
  font-weight:bold;
}

.button {
  margin-top: 1rem;
  font-weight: bold;
}

/* error text message alert */
.alert {
  background: #f2dada;
  padding: 5px;
  font-size: .9em;
  margin-bottom: 15px;
  display: inline-block;
  animation: 2s alertAnim forwards;
}

/* animation effect for error text message alert */
@keyframes alertAnim {
  from {
    opacity:0;
    transform: translateY(-20px);
  }
  to {
    opacity:1;
    transform: translateY(0);
  }
}

   

CSS Style applied HTML Form

    Image - CSS Style applied HTML Form

1.5. Integrating Rective Form Class

  • To work with reactive/dynamic forms we need to import 'ReactiveFormsModule' which provides bunch of classes/directives/utilities necessary to build reactive/dynamic forms
  1. In app.module.ts: import { ReactiveFormsModule } from '@angular/forms';
    • also add to imports: [ ReactiveFormsModule ]

Syntax & Example: app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

1.6. Composing Component Class with ReactiveForms building blocks

  • FormGroup and FormControl are two important building blocks classes for reactive/dynamic forms
    • In Reactive forms, the form is represented by model in component class, FormGroup and FormControl classes used to make that model
    • FormGroup represents whole/entire form ( form is instance of FormGroup class )
    • FormControl represents each form field ( form fields are instance of FormControl class )
    • FormBuilder handle form control creation, dynamic/run time field/FormControl creation
    • Validators helps to setup validation on each form control
  1. In app.component.ts import { FormBuilder, FormGroup, Validators } from '@angular/forms'; and write following logical code to build reactive form

Syntax & Example: app.component.ts

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

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

export class AppComponent {

  simpleBegReactiveForm: FormGroup;     // form
  formSubmitPost: any;                  // A property for our submitted form
  name: string = '';                    // name text
  description: string = '';             // description text

  // create a FormBuilder dependecy injection
  constructor(private fb: FormBuilder) {

    // reference FormBuilder instance to control the validation of each form field
    this.simpleBegReactiveForm = fb.group({
      'name': ['', Validators.required],
      'description': ['', [Validators.required, Validators.minLength(30), Validators.maxLength(100)]],
      'validate': ''
    });

  }

  // handler to submit form
  submitFormData(formSubmitPost) {
    this.description = formSubmitPost.description;
    this.name = formSubmitPost.name;
  }

}

1.7. Update HTML Form (bind Model with View)

  • Lets use and apply class members created in app.component.ts for specific field/controls and bind with form and input fields, ([formGroup]="simpleBegReactiveForm", formControlName="name" ...)

Syntax & Example: app.component.html

<!-- show this template if name property not exist or form not submitted -->
<div *ngIf="!name; else formsubmitdata">

  <!-- associate the model with view -->
  <form [formGroup]="simpleBegReactiveForm" (ngSubmit)="submitFormData(simpleBegReactiveForm.value)">

    <div class="form-container">

      <div class="row columns">
        
        <h1>Beginners Simple Reactive Form</h1>

        <!-- name -->
        <label>Name
          <input type="text" placeholder="Enter Name" formControlName="name">
        </label>

        <!-- description -->
        <label>Description
          <textarea formControlName="description"></textarea>
        </label>

        <!-- checkbox -->
        <label for="validate">Minimum of 5 Characters Name required</label>
        <input type="checkbox" name="validate" value="1" formControlName="validate" > ON

        <!-- submit button -->
        <input type="submit" class="button expanded" value="Submit Form" [disabled]="!simpleBegReactiveForm.valid">

      </div>

    </div>

  </form>

</div>

   

Bind Model with View

    Image - Bind Model with View

1.8. Adding/Showing Validation Messages

  • Now add required validation message text as a alert and use interpolation to show class member

Syntax & Example: app.component.html

<!-- show name alert text -->
<div class="alert" *ngIf="!simpleBegReactiveForm.controls['name'].valid && simpleBegReactiveForm.controls['name'].touched">{{ titleAlertText }}</div>

<!-- show name description text -->
<div class="alert" *ngIf="!simpleBegReactiveForm.controls['description'].valid && simpleBegReactiveForm.controls['description'].touched">{{ descriptionAlertText }}</div>

Syntax & Example: app.component.ts

titleAlertText = 'Name field is required';
descriptionAlertText = 'Specify Description between 30 to 100 characters';

   

Shwoing alert error message text

    Image - Shwoing alert error message text

1.9. Implement Dyanmic Validation with checkbox

  • Sometimes we need to perform form validation based on user input
  • Like in current form, If checkbox is ON than Name must required with 5 characters else any characters
  • We need to track value of checkbox and conditionaly set status of other field
    • valueChanges property helps to track the current value of any controls as a observables
    • setValidators methods - set desired validators to formControl/field
    • clearValidators methods - clears validators from formControl/field
  • Finally we need to invoke/call updateValueAndValidity method to reflect latest status

Syntax & Example: app.component.ts

ngOnInit() {

  // subscribe checkbox
  this.simpleBegReactiveForm.get('validate').valueChanges.subscribe(

  (validate) => {

    if (validate == '1') {
      // name field set/unset `required` validators
      this.simpleBegReactiveForm.get('name').setValidators([Validators.required, Validators.minLength(5)]);
      this.titleAlertText = 'Specify Name with 5 characters';
    } else {
      this.simpleBegReactiveForm.get('name').setValidators(Validators.required);
      this.titleAlertText = 'Name field is required';
    }
    // to reflect latest correct status
    this.simpleBegReactiveForm.get('name').updateValueAndValidity();

  });
}

   

Checkbox dynamic validator error

    Image - Checkbox dynamic validator error


   

Checkbox dynamic validator success

    Image - Checkbox dynamic validator success


   

Submit Data success screen

    Image - Submit Data success screen

1.10. Final working code

Syntax & Example: index.html

<!doctype html>
  <html lang="en">

    <head>
      <meta charset="utf-8">
      <title>AngularReactiveFormsTutorial</title>
      <base href="/">

      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="icon" type="image/x-icon" href="favicon.ico">

      <!-- link/import/include zurb foundation -->
      <link rel="stylesheet" href="./assets/library/foundation.min.css">

    </head>

    <body>
      <app-root></app-root>
    </body>
    
  </html>

Syntax & Example: app.component.html

<!-- show this template if name property not exist or form not submitted -->
<div *ngIf="!name; else formsubmitdata">

  <!-- associate the model with view -->
  <form [formGroup]="simpleBegReactiveForm" (ngSubmit)="submitFormData(simpleBegReactiveForm.value)">

    <div class="form-container">

      <div class="row columns">
        
        <h1>Beginners Simple Reactive Form</h1>

        <!-- name -->
        <label>Name
          <input type="text" placeholder="Enter Name" formControlName="name">
        </label>

        <!-- show name alert text -->
        <div class="alert" *ngIf="!simpleBegReactiveForm.controls['name'].valid && simpleBegReactiveForm.controls['name'].touched">{{ titleAlertText }}</div>

        <!-- description -->
        <label>Description
          <textarea formControlName="description"></textarea>
        </label>
        
        <!-- show name description text -->
        <div class="alert" *ngIf="!simpleBegReactiveForm.controls['description'].valid && simpleBegReactiveForm.controls['description'].touched">{{ descriptionAlertText }}</div>

        <!-- checkbox -->
        <label for="validate">Minimum of 5 Characters Name required</label>
        <input type="checkbox" name="validate" value="1" formControlName="validate" > ON

        <!-- submit button -->
        <input type="submit" class="button expanded" value="Submit Form" [disabled]="!simpleBegReactiveForm.valid">

      </div>

    </div>

  </form>

</div>

<!-- show this template if name property exists or form submitted -->
<ng-template #formsubmitdata>

  <div class="form-container">

    <div class="row columns">

      <h2>You Entered Name: {{ name }}</h2>

      <p>Your Description Details: {{ description }}</p>

    </div>

  </div>
  
</ng-template>

Syntax & Example: styles.css

/* You can add global styles to this file, and also import other style files */

/* apply google font family */
@import url('https://fonts.googleapis.com/css?family=ZCOOL+XiaoWei');

body {
  background:rgba(148, 134, 93, 0.35);;
  /* font-family: 'ZCOOL XiaoWei', serif !important; */
}

* {
  font-family: 'ZCOOL XiaoWei', serif !important;
}

.form-container {
  display:block;
  width:90%;
  padding:2em;
  margin: 2em auto;
  background:#fff;
}

h1 {
  text-align: center;
  margin-bottom: 1rem;
  font-weight:bold;
}

.button {
  margin-top: 1rem;
  font-weight: bold;
}

/* error text message alert */
.alert {
  background: #f2dada;
  padding: 5px;
  font-size: .9em;
  margin-bottom: 15px;
  display: inline-block;
  animation: 2s alertAnim forwards;
}

/* animation effect for error text message alert */
@keyframes alertAnim {
  from {
    opacity:0;
    transform: translateY(-20px);
  }
  to {
    opacity:1;
    transform: translateY(0);
  }
}

Syntax & Example: app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Syntax & Example: app.component.ts

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

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

export class AppComponent {

  simpleBegReactiveForm: FormGroup;     // form
  formSubmitPost: any;                  // A property for our submitted form
  name: string = '';                    // name text
  description: string = '';             // description text

  titleAlertText = 'Name field is required';
  descriptionAlertText = 'Specify Description between 30 to 100 characters';

  // create a FormBuilder dependecy injection
  constructor(private fb: FormBuilder) {

    // reference FormBuilder instance to control the validation of each form field
    this.simpleBegReactiveForm = fb.group({
      'name': ['', Validators.required],
      'description': ['', [Validators.required, Validators.minLength(30), Validators.maxLength(100)]],
      'validate': ''
    });

  }

  // handler to submit form
  submitFormData(formSubmitPost) {
    this.description = formSubmitPost.description;
    this.name = formSubmitPost.name;
  }
  
  ngOnInit() {

    // subscribe checkbox
    this.simpleBegReactiveForm.get('validate').valueChanges.subscribe(

    (validate) => {

      if (validate == '1') {
        // name field set/unset `required` validators
        this.simpleBegReactiveForm.get('name').setValidators([Validators.required, Validators.minLength(5)]);
        this.titleAlertText = 'Specify Name with 5 characters';
      } else {
        this.simpleBegReactiveForm.get('name').setValidators(Validators.required);
        this.titleAlertText = 'Specify Name with 5 characters';
      }
      // to reflect latest correct status
      this.simpleBegReactiveForm.get('name').updateValueAndValidity();

    });
  }

}