Skip to content

Autofocus in Angular

Antofocus controllo in Single Page Application

TypeScript, Angular, Directive2 min read

Autofocus

Introduzione

Ogni volta che si visualizza un form con un campo di input, per migliorare l'utilizzo dell'applicazione, è utile attivare il focus sul primo elemento in cui l'utente può scrivere.

Gli elementi HTML hanno già l'attributo autofocus. Ad esempio l'elemento input si basa sull'interfaccia HTMLInputElement che contiene un valore booleano autofocus. Quando questo valore è true l'elemento html prende il focus quando il form è visualizzato.

Ci sono però alcuni problemi con l'utilizzo dell'autofocus nativo:

  1. Funziona solo al caricamento iniziale della pagina. Tipicamente un'applicazione web fatta con angular è una single web application (SPA). In una SPA possiamo cambiare cambiare pagina senza ricaricare la pagina html ed in questo caso l'attributo nativo non funzionerà.

  2. Funziona solo per componenti renderizzati al caricamento. Quindi, se mettiamo l'elemento html con l'autofocus dietro una direttiva *ngIf che non lo visualizza, quando l' *ngIf sarà true l'autofocus non funzionerà.

In angular abbiamo due modi per risolvere il problema.

Esempio

Immaginiamo di avere un campo di input per gestire il nome di una persona:

1<input type="text" name="firstName" [ngModel]>

Soluzione popolare

Possiamo referenziare il campo di input con una template reference variable #firstName ed un decoratore @ViewChild()

1// template
2<input type="text" name="firstName" [ngModel] #firstName>
3
4// nella classe
5@ViewChild('firstName', { static: false }) firstName!: ElementRef<HTMLInputElement>;

A questo punto possiamo attivare il focus richiamando il metodo nativo dell'elemento:

1this.firstName.nativeElement.focus();

Questa chiamata tipicamente verrà inserita nell'hook che viene attivato dopo che la vista del componente è stata inizializzata: ngAfterViewInit

Questa soluzione funziona, però utilizzare ElementRef ci espone a rischi di sicurezza. Il team di angular nella documentazione dichiara:

Permitting direct access to the DOM can make your application more vulnerable to XSS attacks. Carefully review any use of ElementRef in your code. For more detail, see the Security Guide.

Soluzione sicura

Un modo migliore è utilizzare Renderer2: un servizio fornito dal core package di angular per permettere manipolazioni del DOM sicure.

Iniettiamo il servizio nel costruttore:

1constructor(private renderer: Renderer2) {}

Poi possiamo impostare il focus con:

1this.renderer.selectRootElement(this.input.nativeElement).focus();

Soluzione per focus elemento non caricato

Però ci possiamo trovare nel caso (numero 2) in cui il componente non è visualizzato temporanemente a causa di un *ngIf. In questo caso immaginiamo di creare un button per attivare l'elemento e dargli il focus. Nel componente creiamo un metodo activateInput() che attiverà l'input al click del button. Possiamo utilizzare entrambe le soluzione precedenti e nell'esempio utilizziamo la senconda con Renderer2:

1// template
2<input *ngIf="show" type="text" name="firstName" [ngModel] #firstName>
3<button (click)="activateInput()" >Attiva<button>
4
5// class component
6show = false;
7
8activateInput(): void {
9 this.show = true;
10 this.renderer.selectRootElement(this.myInput.nativeElement).focus();
11}

Se facciamo una prova possiamo vedere che questo metodo così non funziona. Il problema è che il cambio dell' *ngIf e il focus sono eseguiti nello stesso ciclo di rendering prima di attivare una change detection.

Per far funzionare questo metodo ci sono due soluzioni:

  1. possiamo eseguire il focus dopo qualche millisecondo
  2. oppure possiamo forzare la change detection del componente.

Focus "ritardato"

Un esempio della prima soluzione:

1// class componente
2show = false;
3
4activateInput(): void {
5 this.show = true;
6 setTimeout(() => {
7 this.renderer.selectRootElement(this.myInput.nativeElement).focus();
8 }, 200);
9}

Focus "sincrono"

Per la seconda soluzione dobbiamo iniettare il servizio ChangeDetectionRef nel costruttore:

1constructor(private cd: ChangeDetectorRef) {}
2
3activateInput(): void {
4 this.show = true;
5 this.cd.detectChanges();
6 this.renderer.selectRootElement(this.myInput.nativeElement).focus();
7}

Direttiva

Per semplificare l'utilizzo di questi metodi possiamo creare una direttiva: appAutofocus.

1import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
2
3@Directive({
4 selector: '[appAutofocus]',
5})
6export class AutofocusDirective {
7 @Input('appAutofocus') isFocused: boolean;
8
9 constructor(private host: ElementRef, private renderer: Renderer2) {}
10
11 ngAfterViewInit() {
12 if (this.isFocused) {
13 this.renderer.selectRootElement(this.host.nativeElement).focus();
14 }
15 }
16}

La direttiva si potrà utilizzare direttamente nell'elemento in cui vogliamo il focus:

1// template
2<input type="text" name="firstName" [ngModel] #firstName [appAutofocus]="true">

Esempio

Il codice dell'esempio può essere trovato qui:

Codice

Conclusioni

Con Angular è molto semplice attivare il focus di un elemento in automatico. Attenzione però a non abusare di questa pratica. L'assegnazione automatica del focus potrebbe causare problemi di usabilità e di accessibilità, soprattutto per utenti che utilizzano screen reader. In generale per i form semplici, non ci sono problemi.

Riferimenti

MDN Web Docs - <input>: The Input (Form Input) element

Roberto Zaniboni © 2025
Made with ❤️