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') 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);
setTimeout(() => element.focus(), 0);
Thank you @MrBlaise for the solution!
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.
Here is a directive that you can use in any component:
import { NgZone, Directive, ElementRef, AfterContentInit, Renderer2 } from '@angular/core';
@Directive({
selector: '[appFocus]'
})
export class FocusDirective implements AfterContentInit {
constructor(private el: ElementRef, private zone: NgZone, private renderer: Renderer2) {}
ngAfterContentInit() {
this.zone.runOutsideAngular(() => setTimeout(() => {
this.renderer.selectRootElement(this.el.nativeElement).focus();
}, 0));
}
}
Use:
<input type="text" appFocus>
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);
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();
}