Autofocus is a simple HTML attribute, but doesn’t work as expected with Angular apps. Let’s learn why with some different approaches to solve the problem!
Autofocus Attribute
Add the autofocus attribute to an Angular input form field.
<input type="text" id="firstName" name="firstName" formControlName="firstName" autofocus>
If you refresh the page, you will see that the form field has focus. Problem solved right?! Not so fast!
Try starting on a different page first and then navigating to the page with the form. The field has no focus. What happened?
Autofocus happens as soon as the page is loaded. An Angular application is a single page application (SPA). Once that single page is loaded, that’s it! You’ve lost your chance to bring focus to any future navigation to other pages.
So what can we do? Read on!
Template Variable
Create a template variable on the input field called firstNameField. Remember that template variables are prefixed with a pound sign.
<input type="text" id="firstName" name="firstName" formControlName="firstName" #firstNameField>
Within the component, you’ll need to import the following:
import { AfterViewInit, ElementRef, ViewChild } from '@angular/core';
We want to access the input field from the view. We can use ViewChild and reference the template variable we previously created called firstNameField. Then give it a name also called firstNameField, though it doesn’t have to be. We’ll define it’s type to be ElementRef, specifically an HTML input element.
@ViewChild('firstNameField') firstNameField: ElementRef<HTMLInputElement>;
Your first thought might be to do something within the OnInit lifecycle hook. For this particular scenario, that won’t work. We need to make sure the form field exists on the page before trying to do anything with it. For that we need the AfterViewInit lifecycle hook.
Make sure to implement AfterViewInit on your component.
export class ExampleComponent implements AfterViewInit, OnInit {
...
}
Within the ngAfterViewInit lifecycle hook, let’s access the underlying native element and call the focus function.
ngAfterViewInit() {
this.firstNameField.nativeElement.focus();
}
Try starting on a different page first and then navigating to the page with the form. The form field now has focus!
Now imagine you had other form fields where you wanted to do this same thing. It gets a little repetitive doing the above changes for each component. That’s where an Angular directive can help!
Autofocus Directive
Create the following directive.
import { AfterViewInit, Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[appAutofocus]'
})
export class AutofocusDirective implements AfterViewInit {
constructor(private element: ElementRef<HTMLInputElement>) { }
ngAfterViewInit(): void {
this.element.nativeElement.focus();
}
}
We’re doing something similar here like we did in the component. We use the AfterViewInit lifecycle hook to bring focus to the element.
To make this directive usable across any module, let’s create it as its own module so it can be imported into any module where it’s needed.
import { NgModule } from '@angular/core';
import { AutofocusDirective } from './autofocus.directive';
@NgModule({
declarations: [AutofocusDirective],
exports: [AutofocusDirective]
})
export class AutofocusDirectiveModule { }
To use the directive, first import it into the module where you want to use it.
import { AutofocusDirectiveModule } from 'path/to/autofocus.directive.module';
@NgModule({
...
imports: [
...
AutofocusDirectiveModule,
...
]
})
export class ExampleModule { }
Add the directive attribute, which is called appAutofocus, to the input field.
<input type="text" id="firstName" name="firstName" formControlName="firstName" appAutofocus>
Instead of doing the steps outlined in the prior Template Variable section, you can now reuse this directive wherever you need to add focus to a form field!
Visit our website at https://nightwolf.dev and follow us on Twitter!