Angular2+ autofocus input element

AngularTypescript

Angular Problem Overview


How can I autofocus input element? Similar to this question, but not with AngularDart. Something like this:

<input type="text" [(ngModel)]="title" [focus] />
//or 
<input type="text" [(ngModel)]="title" autofocus />

Does Angular2 has any build in support for this feature?

Best close question is this one, but is there any shorter/easier solution, since I do not have "list of input boxes". In provided link *ngFor="#input of inputs" is used, but I only have 1 input in control template.

Angular Solutions


Solution 1 - Angular

This is my current code:

import { Directive, ElementRef, Input } from "@angular/core";

@Directive({
    selector: "[autofocus]"
})
export class AutofocusDirective
{
    private focus = true;

    constructor(private el: ElementRef)
    {
    }

    ngOnInit()
    {
        if (this.focus)
        {
            //Otherwise Angular throws error: Expression has changed after it was checked.
            window.setTimeout(() =>
            {
                this.el.nativeElement.focus(); //For SSR (server side rendering) this is not safe. Use: https://github.com/angular/angular/issues/15008#issuecomment-285141070)
            });
        }
    }

    @Input() set autofocus(condition: boolean)
    {
        this.focus = condition !== false;
    }
}

use case:

[autofocus] //will focus
[autofocus]="true" //will focus
[autofocus]="false" //will not focus

Outdated code (old answer, just in case):
I ended up with this code:

import {Directive, ElementRef, Renderer} from '@angular/core';

@Directive({
    selector: '[autofocus]'
})
export class Autofocus
{
    constructor(private el: ElementRef, private renderer: Renderer)
    {        
    }

    ngOnInit()
    {        
    }

    ngAfterViewInit()
    {
        this.renderer.invokeElementMethod(this.el.nativeElement, 'focus', []);
    }
}

If I put code in ngOnViewInit it does not work. Code also use best practices, since calling focus on element directly is not recommended.

Edited (conditional autofocus):
A few days ago I needed conditional auto focus, because I hide first autofocus element and I want to focus another, but only when first is not visible, and I ended with this code:

import { Directive, ElementRef, Renderer, Input } from '@angular/core';

@Directive({
    selector: '[autofocus]'
})
export class AutofocusDirective
{
    private _autofocus;
    constructor(private el: ElementRef, private renderer: Renderer)
    {
    }

    ngOnInit()
    {
    }

    ngAfterViewInit()
    {
        if (this._autofocus || typeof this._autofocus === "undefined")
            this.renderer.invokeElementMethod(this.el.nativeElement, 'focus', []);
    }

    @Input() set autofocus(condition: boolean)
    {
        this._autofocus = condition != false;
    }
}

Edited2:
Renderer.invokeElementMethod is deprecated and new Renderer2 does not support it. So we are back to native focus (which doesn't work outside DOM - SSR for example!).

import { Directive, ElementRef, Input } from '@angular/core';

@Directive({
    selector: '[autofocus]'
})
export class AutofocusDirective
{
    private _autofocus;
    constructor(private el: ElementRef)
    {
    }

    ngOnInit()
    {
        if (this._autofocus || typeof this._autofocus === "undefined")
            this.el.nativeElement.focus();      //For SSR (server side rendering) this is not safe. Use: https://github.com/angular/angular/issues/15008#issuecomment-285141070)
    }

    @Input() set autofocus(condition: boolean)
    {
        this._autofocus = condition != false;
    }
}

use case:

[autofocus] //will focus
[autofocus]="true" //will focus
[autofocus]="false" //will not focus

Solution 2 - Angular

autofocus is a native html feature that should work at least for page initialization. However it fails to work with many angular scenarios, especially with *ngIf.

You can make a really simple custom directive to get desired behaviour.

import { Directive, OnInit, ElementRef } from '@angular/core';

@Directive({
  selector: '[myAutofocus]'
})
export class AutofocusDirective implements OnInit {

  constructor(private elementRef: ElementRef) { };

  ngOnInit(): void {
    this.elementRef.nativeElement.focus();
  }

}

The above directive works for my use cases.

How to use

<input *ngIf="someCondition" myAutofocus />

EDIT: There seems to be usecases where it is to early to call focus in the OnInit lifecycle method. If that is the case, change to OnAfterViewInit instead.

Solution 3 - Angular

You could assign the input element a template reference variable #myInput:

<input type="text" [(ngModel)]="title" #myInput />

Let your component implement AfterViewInit, grab the reference of the input element with the ViewChild annotation and focus your element in the ngAfterViewInit hook:

export class MyComponent implements AfterViewInit {
    @ViewChild("myInput") private _inputElement: ElementRef;

    [...]

    ngAfterViewInit(): void {
        this._inputElement.nativeElement.focus();
    }
}

Solution 4 - Angular

The following directive works for me using Angular 4.0.1

import {Directive, ElementRef, AfterViewInit} from '@angular/core';

@Directive({
  selector: '[myAutofocus]'
})
export class MyAutofocusDirective implements AfterViewInit {
  constructor(private el: ElementRef)
  {
  }
  ngAfterViewInit()
  {
    this.el.nativeElement.focus();
  }
}

use it like this:

<md-input-container>
    <input mdInput placeholder="Item Id" formControlName="itemId" name="itemId" myAutofocus>
</md-input-container>

The option of using OnInit lifecycle event did not work for me. I also tried using the Renderer in the other answer which didn't work for me.

Solution 5 - Angular

<input type="text" [(ngModel)]="title" #myInput />
{{ myInput.focus() }}

just add {{ myInput.focus() }} right after input inside template

Solution 6 - Angular

If you don't need true/false functionality, but want autofocus set always, there's a shorter implementation to Makla's solution:

autofocus.directive.ts:

import { Directive, ElementRef, Input, OnInit } from '@angular/core';

@Directive({
    selector: '[autofocus]'
})

export class AutofocusDirective implements AfterViewInit {

    constructor(private el: ElementRef) {
    }

    ngAfterViewInit() {
        // Otherwise Angular throws error: Expression has changed after it was checked.
        window.setTimeout(() => {
            this.el.nativeElement.focus();
        });
    }
}

use case:

<input autofocus> //will focus

Using AfterViewInit instead of OnInit makes the cursor placed after the content inside input field, should it get populated.

Remember to declare and export autofocus directive in your module!

Solution 7 - Angular

Starting from IE11, as all other modern browsers, the native HTML autofocus Attribute for inputs should work fine too without tying on Angular:

<input autofocus>
 
<input type="text" [(ngModel)]="title" autofocus>
UPDATE

As mentioned by @adrug's comment below, this approach has a pitfall. It will only work once, when the page is loaded.

As explained in Netanel Basal's article, a better way is to implement it as an Angular Directive. As it was already suggested by other answers.

Solution 8 - Angular

Try this simple but effective function.:

function addFocusInput() {
  document.getElementById('text-focus').focus();
}

addFocusInput();

<input id="text-focus" type="text" placeholder=""/>

Solution 9 - Angular

I know this is an old post but for others looking for a newer answer: I was using an angular material dialog and it was auto-selecting the close button not the input.

Using cdk-focus-start (part of the CDK) fixes this ... with no additional code.

Solution 10 - Angular

My solution :

 <input type="text" id="searchInput">
// put focus on element with id 'searchInput', try every 100ms and retry 30 times
this.focus('searchInput',30,100);
focus( idElement:string, maxNbRetries:number, intervalMs:number){

    let nbRetries = 0;
    let elt = null;
    const stop$ = new Subject<boolean>();
    const source = interval(intervalMs);
    const source$ = source.pipe(
      tap(item=>{
        elt = document.getElementById(idElement);
        nbRetries++;
        if(nbRetries>maxNbRetries){
          stop$.next(true);
          console.log(`unable to put the focus on the element !`)
        }
      }),
      filter(item=>elt !=null),
      map(item=>{
        elt.focus();
        stop$.next(true);
      }),
      takeUntil(stop$)

    ).subscribe();
  }

Focus does not work well with angular lifecycle. To force it on a field, i run an observable that emits every 'intervalMs'. If the element has been rendered, i can find it by its id. After that, i can set the focus. If nbRetries > maxNbRetries or element id is found, i stop the observable with the takeUntil operator.

Solution 11 - Angular

Smart autofocus (dynamic focus) directive

Here's my take on Angular autofocus directive.

The following directive accepts boolean value as an input and focuses the element based on the evaluated boolean expression so the element could be focused dynamically.

Also, the directive could be applied to the input/button/select/a elements directly or to any parent element as well. It will search the DOM for the first suitable element to focus automatically.

The code

import { AfterViewInit, Directive, ElementRef, Input, NgModule } from '@angular/core';


const focusableElements = [
  'input',
  'select',
  'button',
  'a',
];


@Directive({
  selector: '[autofocus]',
})
export class AutofocusDirective implements AfterViewInit {

  @Input()
  public set autofocus(shouldFocus: boolean) {
    this.shouldFocus = shouldFocus;
    this.checkFocus();
  }

  private shouldFocus = true;


  constructor(
    private readonly elementRef: ElementRef
  ) {
  }


  public ngAfterViewInit() {
    this.checkFocus();
  }


  private checkFocus() {

    if (!this.shouldFocus) {
      return;
    }

    const hostElement = (
      <HTMLElement>
      this.elementRef.nativeElement
    );

    if (!hostElement) {
      return;
    }

    if (focusableElements.includes(
      hostElement.tagName.toLowerCase())
    ) {
      hostElement.focus?.();

    } else if (hostElement?.querySelector) {

      for (const tagName of focusableElements) {
        const childElement = (
          <HTMLInputElement>
            hostElement.querySelector(tagName)
        );
        if (childElement) {
          childElement?.focus?.();
          break;
        }
      }

    }

  }

}


@NgModule({
  declarations: [
    AutofocusDirective,
  ],
  exports: [
    AutofocusDirective,
  ],
})
export class AutofocusModule {
}

Usage examples

<!-- These are equivalent: -->
<input type="text" autofocus>
<input type="text" [autofocus]>
<input type="text" [autofocus]="true">

<!-- Conditional (dynamic) focusing: -->
<input type="text" [autofocus]="shouldBeFocused">
<input type="text" name="username" [autofocus]="focusedField === 'username'">

<!-- Using parent element: -->
<fieldset autofocus>
  <label>
    Username:
    <input type="text">
  </label>
</fieldset>

Notice

Be advised, that this code will fully work only in modern browser environment, however, it shouldn't throw in other environments (graceful degradation).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMaklaView Question on Stackoverflow
Solution 1 - AngularMaklaView Answer on Stackoverflow
Solution 2 - AngularHampusView Answer on Stackoverflow
Solution 3 - AngularrinukkusuView Answer on Stackoverflow
Solution 4 - AngularMichael LangView Answer on Stackoverflow
Solution 5 - AngularSergey GurinView Answer on Stackoverflow
Solution 6 - AngularTanel JõeäärView Answer on Stackoverflow
Solution 7 - AngularSalem OuerdaniView Answer on Stackoverflow
Solution 8 - AngularWilder Chavez LamadridView Answer on Stackoverflow
Solution 9 - AngularTim.BurnellView Answer on Stackoverflow
Solution 10 - AngularRhumain VermoView Answer on Stackoverflow
Solution 11 - AngularSlava Fomin IIView Answer on Stackoverflow