Focus an element after it appears via ngIf

2020-02-14 08:30发布

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?

2条回答
够拽才男人
2楼-- · 2020-02-14 09:03

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
  }
}
查看更多
我想做一个坏孩纸
3楼-- · 2020-02-14 09:03

Yes, *ngIf and ViewChild don't play well together. I did a course on ViewChild and did an entire section just on handing the *ngIf.

One option is to use the hidden attribute instead of *ngIf.

Another option is to bind to a setter (similar to how you bound to a function):

enter image description here

查看更多
登录 后发表回答