Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Focus an element after it appears via ngIf

I have a button that, when clicked, is replaced with an input field and a confirmation button, then when input is finished it's replaced with the original button again. When that happens, I want it to focus the original button after it appears (some users have requested better support for tab-navigation), but I can't seem to get it to do that consistently. The best I've been able to do is this:

// component.html
<button #durationButton *ngIf="!enteringDuration" (click)="enterDuration()">Enter Duration</button>
<ng-container *ngIf="enteringDuration">
    <input type="number" [(ngModel)]="duration" (keyup.enter)="setDuration()">
    <button (click)="setDuration()">&#10003;</button>
</ng-container>
// component.ts
@ViewChild("durationButton") durationButton: ElementRef
duration: number
enteringDuration = false
shouldFocusDurationButton = false

ngAfterContentChecked () {
    if (this.shouldFocusDurationButton && this.durationButton) {
        this.shouldFocusDurationButton = false
        this.durationButton.nativeElement.focus()
    }
}

enterDuration () {
    this.enteringDuration = true
}
setDuration () {
    this.enteringDuration = false
    this.shouldFocusDurationButton = true
}

If I click or press enter on the confirmation button, focus moves to the original button as soon as it appears, but if I press enter in the input field the button appears but for some reason it doesn't gain focus until I move the mouse. How do I make it work immediately for both?

like image 211
John Montgomery Avatar asked Jul 27 '18 21:07

John Montgomery


1 Answers

You can use ViewChildren and the QueryList.changes event to be notified when the button is added to or removed from the view. If the QueryList contains the button element, you can set the focus on it. See this stackblitz for a demo. Suggestion: you may want to do something similar to set the focus on the input field when it becomes visible.

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

export class AppComponent implements AfterViewInit {

  @ViewChildren("durationButton") durationButton: QueryList<ElementRef>;

  enteringDuration = false

  ngAfterViewInit() {
    this.setFocus(); // If the button is already present...
    this.durationButton.changes.subscribe(() => {
      this.setFocus();
    });
  }

  setFocus() {
    if (this.durationButton.length > 0) {
      this.durationButton.first.nativeElement.focus();
    }
  }

  enterDuration() {
    this.enteringDuration = true
  }

  setDuration() {
    this.enteringDuration = false
  }
}
like image 64
ConnorsFan Avatar answered Oct 31 '22 11:10

ConnorsFan