Angular provides couple of ways to bind validation to form elements. One way is using ngModel directives and it’s applicable for template driven forms. Next way is using FormControl or FormGroup directives and its applicable for reactive forms.

We are going to learn here how to show validation error message for the required fields in reactive form when user hit submit button without touching any fields in the form.

Reactive form example with submit button disabled when form is invalid

Reactive form validation examples available in the internet are mostly submit button will be disabled when form is invalid. When you are targetting your application for visual users, you can disable button. Visual user will be able to identify button disable state and interact form fields first.

Login form without Submit button disable

Application should be accessible when you are targetting for all type of users, especially visually challenged user. In this case we cannot disable form fields, and let the user hit submit button and throw the validation message if form fields are invalid.

Now let’s see how to implement this. In the initial state form is pristine and invalid if there is any form fields has validation and form is not submitted. But in angular there is no flag in FormGroup directive to differenciate that form is submitted or not.

I am going a create a new directive with selector [formGroup] and add public variable submitted to capture form submitted status and take action on form fields when needed.

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

export class AppFormGroup extends FormGroup {
    submitted: boolean;
}
import { Directive, Input, OnChanges, SimpleChanges, HostListener, HostBinding } from '@angular/core';
import { AppFormGroup } from './form-group.class';

@Directive({ selector: '[formGroup]' })
export class AppFormGroupDirective implements OnChanges {

    @Input() formGroup: AppFormGroup;
    @HostBinding('class.error') hasError = false;
    constructor() {
    }

    ngOnChanges(changes: SimpleChanges): void {
        if (changes.formGroup && changes.formGroup.firstChange) {
            this.formGroup.submitted = false;
        }
    }

    @HostListener('submit')
    onSubmit() {
        this.formGroup.submitted = true;
        this.hasError = this.formGroup.invalid;
    }

    @HostListener('reset')
    onReset(){
        this.formGroup.submitted = false;
        this.hasError = false;
    }
}

This directive will share same selector as angular directive, but additionally it will have submitted variable value as true when form is submitted and false when form is initialized or when form is reset. This directive is also adding class “error” to host element ( <form>) when form is invalid after submit event triggered. I am using semantic-ui to decorate form fields, I have to add “error” class in form and fields to show validation errors. You can change the class name as per your need. Now let’s take a look at template

<div class="ui grid">
  <div class="ui four wide centered column">
    <form name="loginForm" class="ui form" novalidate [formGroup]="loginForm" (submit)="handleFormSubmit()">
      <h1 class="ui dividing header medium">Login Form</h1>
      <div class="ui error message" role="alert" *ngIf="loginForm?.submitted && loginForm.controls?.username.errors">
        Please enter a login name.
      </div>
      
      <div class="required field error" [ngClass]="{'error': (loginForm?.submitted && loginForm.controls?.username.invalid) || (loginForm.controls?.username.touched && loginForm.controls?.username.invalid) }">
        <label for="loginField">Login name</label>
        <input id="loginField" type="text" placeholder="username" formControlName="username">        
        <p *ngIf="loginForm.controls?.username?.touched && loginForm.controls?.username?.errors?.required" class="field-error">Please enter username</p>
      </div>

      <div class="field">
          <button class="ui button primary" type="submit">Submit</button>
          <button class="ui button" type="reset">Clear</button>
      </div>      

    </form>
  </div>
</div>

Here is code for login.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators, FormControl } from '@angular/forms';
import { AppFormGroup } from '../../shared/directives/form-group.class';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styles: [
    `
    .field-error{
      color:#9F3A38;
    }
    `
  ]
})
export class LoginComponent implements OnInit {

  loginForm: AppFormGroup;
  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.loginForm = new AppFormGroup({
      username: new FormControl('', Validators.required)
    });
  }

  handleFormSubmit() {
    console.log('Form Submitted', this.loginForm.value);
  }
}

This is how it will look when user hit submit button without interacting login name field.

Login form with validation message when form submit without touching field

Creating custom directive to handle form submit will be more modular approach and it will be more helpfull when you have nested form. This is just my approach to solve reactive form submit.Thanks for reading my article and let me know your comments. Don’t forget to see this code in action
here or download the code from my github.

Categories: Angular

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.