How to use Angular4 to set focus by element id

I am new to Angular, and am trying to use it to set focus on an input with the id "input1". I am using the following code:

@ViewChild('input1') inputEl: ElementRef;

then later in the component:

 this.inputEl.nativeElement.focus();

But it isn't working. What am I doing wrong? Any help will be much appreciated.


Component

import { Component, ElementRef, ViewChild, AfterViewInit} from '@angular/core';
... 

@ViewChild('input1', {static: false}) inputEl: ElementRef;
    
ngAfterViewInit() {
   setTimeout(() => this.inputEl.nativeElement.focus());
}

HTML

<input type="text" #input1>

One of the answers in the question referred to by @Z.Bagley gave me the answer. I had to import Renderer2 from @angular/core into my component. Then:

const element = this.renderer.selectRootElement('#input1');
setTimeout(() => element.focus(), 0);

Thank you @MrBlaise for the solution!


I also face same issue after some search I found a good solution as @GreyBeardedGeek mentioned that setTimeout is the key of this solution.He is totally correct. In your method you just need to add setTimeout and your problem will be solved.

setTimeout(() => this.inputEl.nativeElement.focus(), 0);

Here is an Angular4+ directive that you can re-use in any component. Based on code given in the answer by Niel T in this question.

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

@Directive({
    selector: '[focusDirective]'
})
export class FocusDirective {
    @Input() cssSelector: string

    constructor(
        private ngZone: NgZone,
        private renderer: Renderer
    ) { }

    ngOnInit() {
        console.log(this.cssSelector);
        this.ngZone.runOutsideAngular(() => {
            setTimeout(() => {
                this.renderer.selectRootElement(this.cssSelector).focus();
            }, 0);
        });
    }
}

You can use it in a component template like this:

<input id="new-email" focusDirective cssSelector="#new-email"
  formControlName="email" placeholder="Email" type="email" email>

Give the input an id and pass the id to the cssSelector property of the directive. Or you can pass any cssSelector you like.

Comments from Niel T:

Since the only thing I'm doing is setting the focus on an element, I don't need to concern myself with change detection, so I can actually run the call to renderer.selectRootElement outside of Angular. Because I need to give the new sections time to render, the element section is wrapped in a timeout to allow the rendering threads time to catch up before the element selection is attempted. Once all that is setup, I can simply call the element using basic CSS selectors.


This helped to me (in ionic, but idea is the same) https://mhartington.io/post/setting-input-focus/

in template:

<ion-item>
      <ion-label>Home</ion-label>
      <ion-input #input type="text"></ion-input>
</ion-item>
<button (click)="focusInput(input)">Focus</button>

in controller:

  focusInput(input) {
    input.setFocus();
  }