Form Submitting & Resetting

During the form submit we need to take care of all the data entered by a user are valid.

We should not allow the user to submit the form until all data entered are valid.

To restrict a user from invalid form submit we will disable the Submit button until all inputs are valid. Once all inputs are valid we will enable Submit button.

Angular forms have properties like value, valid, invalid etc.

Valid property of the form becomes true when all its form controls are valid. Invalid is opposite of valid.

As you can see below, I have disabled the submit button until invalid property of myForm is true.

<button type="submit" [disabled]="myForm.invalid" class="btn btn-primary">Submit</button>

Once all controls are valid, we can submit the form either using ngSubmit directive at form level or using simple click event binding with the submit button.

At the form level you can use as below,

<form [formGroup]="myForm" (ngSubmit)="addProduct(myForm.value)" >
        ...
        ...
</form>

Or with the (click) event of submit button, as shown below

<form [formGroup]=”myForm”>
       ....
       ....
       <button type="submit" [disabled]="myForm.invalid" (click)="addProduct(myForm.value)" class="btn btn-primary">Submit</button>
</form>

Here we will pass the myForm.value as a parameter of addProduct() method. 

myForm.value will give us JSON object of inputs as we have seen in Forms Chapter.

We will use here ProductService to add Product to a server. add addProduct() method in product.service.ts as shown below,

export class ProductService {

  products: Product[] = [];
  ...
  addProduct(product: Product) {
    this.products.push(product);
  }
  ...
}

We will consume the addProduct() method of ProductService in AddProductModelComponent.

To use ProductService in AddProductModelComponent we need to import it and then provide its dependency in constructor() .

Now create the event handler addProduct(product) in AddProductModelComponent, to handle the submit event of a form. As shown below,

import { ProductService } from './../service/product.service';
import { Product } from '../models/product';
...

@Component({
  selector: 'app-add-product-model',
  templateUrl: './add-product-model.component.html',
  styleUrls: ['./add-product-model.component.css']
})
export class AddProductModelComponent implements OnInit {

  ...
  formSubmitted = false;
  ...

  constructor(private productService: ProductService) { }

  addProduct(product: Product) {
    this.productService.addProduct(product);
    this.formSubmitted = true;
  }
  ...

}

Here I have created one formSubmitted flag, which I have used to show a message when a form is submitted.

I have shown the dismissible alert box once form submitted as shown below.

<div class="alert alert-info alert-dismissible " role="alert" *ngIf="formSubmitted">
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
          <span aria-hidden="true">&times;</span>
          <span class="sr-only">Close</span>
        </button>
        <strong>Bingo !!! Form Submitted...</strong>
</div>

If you have installed Bootstrap Snippets Extension then, just write b4-alert-dismissible on an html page, and it will generate the dismissible alert template for you.

We can reset the form using myForm.reset() method.

We can call this method inside the component method or directly use in the click event of Reset button as shown below.

<button class="btn btn-default" (click)="myForm.reset()">Reset</button>
import { ProductService } from './../service/product.service';
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl} from '@angular/forms';
import { Validators} from '@angular/forms';
import { Product } from '../models/product';
@Component({
  selector: 'app-add-product-model',
  templateUrl: './add-product-model.component.html',
  styleUrls: ['./add-product-model.component.css']
})
export class AddProductModelComponent implements OnInit {

  productTypes = ['Laptop', 'Mobile'];

  formSubmitted = false;
  myForm: FormGroup;

  title: FormControl;
  modelName: FormControl;
  color: FormControl;
  productType: FormControl;
  brand: FormControl;
  price: FormControl;

  constructor(private productService: ProductService) { }

  ngOnInit() {

    this.title = new FormControl('', [Validators.required, Validators.minLength(10)]);
    this.modelName = new FormControl();
    this.color = new FormControl('', Validators.pattern('[a-zA-Z]*'));
    this.productType = new FormControl('', Validators.required);
    this.brand = new FormControl('', Validators.required);
    this.price = new FormControl('', [Validators.required, Validators.min(1)]);

    this.myForm = new FormGroup({
      'title': this.title,
      'modelName' : this.modelName,
      'productType' : this.productType,
      'color': this.color,
      'brand': this.brand,
      'price': this.price
    });

  }

  addProduct(product: Product) {

    this.productService.addProduct(product);
    this.formSubmitted = true;

  }

}
<div class="card">
  <div class="card-body">
    <h4 class="card-title">ADD PRODUCT (Model Driven Form)</h4>
    <div class="alert alert-info alert-dismissible " role="alert" *ngIf="formSubmitted">
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
          <span aria-hidden="true">&times;</span>
          <span class="sr-only">Close</span>
        </button>
        <strong>Bingo !!! Form Submitted...</strong>
      </div>
    <div class="row">
      <div class="col-md-6">
        <form [formGroup]="myForm" novalidate>
          <div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" formControlName="title" [ngClass]="{'is-invalid': title.errors && (title.touched || title.dirty), 'is-valid':title.valid}"   >
            <div class="invalid-feedback" >
              <strong *ngIf="title.errors?.required">Title is required</strong>
              <strong *ngIf="title.errors?.minlength">Title length must be greated than {{title.errors?.minlength.requiredLength}} character. {{title.errors?.minlength.requiredLength - title.errors?.minlength.actualLength}} more character required. </strong>
            </div>
          </div>
          <div class="form-group ">
            <label>Model Name</label>
            <input type="text" class="form-control" formControlName="modelName">
          </div>
          <div class="form-group ">
            <label>Color</label>
            <input type="text" class="form-control" formControlName="color" [ngClass]="{'is-invalid': color.errors && (color.touched || color.dirty)}">
            <div class="invalid-feedback" >
              <strong>Only Alphabets are allowed. </strong>   
            </div>
          </div>
          <div class="form-group ">
            <label>Product Type</label>
            <select class="form-control" formControlName="productType" [ngClass]="{'is-invalid': productType.errors && (productType.touched || productType.dirty), 'is-valid':title.valid}" >
              <option *ngFor="let p of productTypes" [value]="p">{{p}}</option>
            </select>
            <div class="invalid-feedback" >
                <strong>Select Product Type.</strong>    
            </div>
          </div>
          <div class="form-group">
            <label>Brand</label>
            <input type="text" class="form-control" formControlName="brand" [ngClass]="{'is-invalid': brand.errors && (brand.touched || brand.dirty), 'is-valid':brand.valid}">
            <div class="invalid-feedback" >
                <strong>Brand Name is required.</strong>    
            </div>
          </div>
          <div class="form-group">
            <label>Price</label>
            <input type="number" class="form-control" formControlName="price" [ngClass]="{'is-invalid': price.errors && (price.touched || price.dirty), 'is-valid':price.valid}">
            <div class="invalid-feedback" >
                <strong *ngIf="price.errors?.required"> Price is required</strong>
                <strong *ngIf="price.errors?.min"> Price should be greated than zero.</strong>    
            </div>
          </div>
          <button type="submit" [disabled]="myForm.invalid" (click)="addProduct(myForm.value)" class="btn btn-primary">Submit</button>
          <button class="btn btn-default" (click)="myForm.reset()">Reset</button>
        </form>
      </div>

      <div class="col-md-6">
        <label>
          <strong>myForm : JSON</strong>
        </label>
        <pre class="alert alert-dark">{{myForm.value | json}}</pre>
      </div>
    </div>
  </div>
</div>
import { MockData } from './../mock-data/mock-product-data';
import { Injectable } from '@angular/core';
import { Product } from '../models/product';

@Injectable()
export class ProductService {

  products: Product[] = [];
  constructor() {
    this.products = MockData.Products;
  }

  getProducts(): Product[] {
    return this.products;
  }

  addProduct(product: Product) {
    this.products.push(product);
  }

  removeProduct(product: Product) {
    let index = this.products.indexOf(product);
    if (index !== -1) {
      this.products.splice(index, 1);
    }
  }
}

In this chapter, we have seen

  • The way to restrict user from invalid submit
  • Valid and Invalid Property of Angular Form
  • Form Submit using ngSubmit and click event of submit button
  • Created addProduct() method in ProductService
  • Created an event handler in add-product-model.component.ts
  • Form Reset with click event binding.