Cory Rylan

My name is , Google Developer Expert, Speaker, Software Developer. Building Design Systems and Web Components.

Follow @coryrylan
Angular

Focus First Invalid Input with Angular Forms

Cory Rylan

- 2 minutes

Updated

This article has been updated to the latest version Angular 17 and tested with Angular 16. The content is likely still applicable for all Angular 2 + versions.

When dealing with required inputs on a form sometimes the user can miss a required field. When this happens, it can be confusing for users what needs to be resolved to continue in the form. With Angular Forms, we can help the user by focusing the first invalid input when they submit the form.

In this example form, we have two inputs. Each input has a required validator to ensure the user has entered a value.

<form [formGroup]="form" (ngSubmit)="submit()">
  <label for="first">First Name</label>
  <input formControlName="first" id="first" />

  <label for="last">Last Name</label>
  <input formControlName="last" id="last" />

  <button>submit</button>
</form>
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup;

  constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      first: ['', Validators.required],
      last: ['', Validators.required]
    });
  }

  submit() {
    if (this.form.valid) {
      console.log(this.form);
    }
  }
}

Our form is using Angular Reactive Forms and the FormBuilder service to create the logic for our form. To add the logic to focus the first invalid input when the user submits, we will create an isolated directive. This directive we will call focusInvalidInput. When this directive is placed on our form it will listen for the submit event to know when to focus our first invalid input.

import { Directive, HostListener, ElementRef } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { FormGroup } from '@angular/forms';

@Directive({
  selector: '[focusInvalidInput]'
})
export class FormDirective {
  constructor(private el: ElementRef) {}

  @HostListener('submit')
  onFormSubmit() {
    const invalidControl = this.el.nativeElement.querySelector('.ng-invalid');

    if (invalidControl) {
      invalidControl.focus();
    }
  }
}

To listen to the submit event on our form element, we can use the @HostListener decorator to call our method when fired. Once we have the event, we can check if there are any invalid inputs. The easiest way to check for invalid inputs is to check for the .ng-invalid CSS selectors that are created in the HTML by Angular.

To check the template of just our form, we can inject the ElementRef service which will provide a reference to our HTML form template. With the template we can use this.el.nativeElement.querySelector('.ng-invalid') to get a list of invalid inputs. If there are any invalid elements, we can then focus the first one in the list.

Once we have the directive created, we need to make sure we place it on the form element that we want the behavior.

<form focusInvalidInput [formGroup]="form" (ngSubmit)="submit()">
  <label for="first">First Name</label>
  <input formControlName="first" id="first" />

  <label for="last">Last Name</label>
  <input formControlName="last" id="last" />

  <button>submit</button>
</form>

If you want this behavior by default globally for all forms in your Angular application you can change the directive selector from [focusInvalidInput] to form Check out the full working demo below!

View Demo Code   
Twitter Facebook LinkedIn Email
 

No spam. Short occasional updates on Web Development articles, videos, and new courses in your inbox.

Related Posts

Angular

Creating Dynamic Tables in Angular

Learn how to easily create HTML tables in Angular from dynamic data sources.

Read Article
Web Components

Reusable Component Patterns - Default Slots

Learn about how to use default slots in Web Components for a more flexible API design.

Read Article
Web Components

Reusable Component Anti-Patterns - Semantic Obfuscation

Learn about UI Component API design and one of the common anti-patterns, Semantic Obfuscation.

Read Article