Angular 8/9 How to programmatically set focus on MatInput

Viewed 791

I have this tiny sample project https://stackblitz.com/edit/angular-material-input-sample-nonw

What I am trying to do is to have user enter a piece of single line text by clicking on a placeholder say [Enter Image Caption] as shown, the user clicks there, and a MatInput element is shown where the user can enter text. When user press Enter, text is saved and show instead of initial placeholder, also, if user goes to another element that text entered gets saved.

I an using ngClass to hide and show placeholder text or MatInput control depending on editMode property of component.

Problems I have:

  • When I click on placeholder text, I have to click again, so this.inputField.nativeElement.focus(); is not doing what's supposed to do.
  • When you click Edit button that's a simple call to this.inputField.nativeElement.focus(); and as you can see it only works when MatInput is on screen (not hidden), i.e. it saves the user one extra click to actually start typing text on input field.

The question becomes: How do I guarantee that when I stop hiding an element (show via toggle variable in [ngClass]), setting the focus on that element would actually work as expected?. Or is there anyway to guarantee that an element is actually shown on screen so that element.focus can work.

2 Answers

I just remembered a trick that I used back when I was working with angular js, and looks like it works here un angular 2+, basically you do a timeout to let angular update the view, so right after changing the toggle variable you do a setTimeout with 0 like this

this.editMode = true; // toggle that shows matInput
setTimeout(() => {
  this.inputField.nativeElement.focus(); // now this actually sets the focus
}, 0)

Here's the working project: https://stackblitz.com/edit/angular-material-input-sample

You can do something like this:
in the template file:

<input #myname>

in the ts file, declare your html element:

@ViewChild('myname') input;

finally use ngAfterViewInit in your component, to wait for the view to initialize before executing the focus on your html elemnt:

ngAfterViewInit() {
  this.input.nativeElement.focus();
}

EDIT:

you can setup a click listener on your <p> element and execute a method afterward:
in you template:

<p (click)="focusInput()">

in you ts file:

focusInput() {
  if (input) {
    this.input.nativeElement.focus();
  }
}
Related