Focus First Invalid Input with Angular Forms
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!