专注于 ngFor 内部的输入

Focus on input inside ngFor

当用户单击按钮时,addElement 方法会将对象推送到 elements 的数组,并创建新的输入字段。是否可以专注于新创建的元素?输入没有独特的 ID。

模板:

<div *ngFor="let foo of elements">
  <input class="input" value="{{ foo.value }}" />
</div>
<button (click)="addElement()"></button>

方法:

public addElement() {
  this.elements.push({ value: 'new' });
}

我在此应用程序中使用 Angular 9。

您需要通过 ViewChildren 装饰器查询所有输入,然后每次 addElement 调用时 ngAfterViewChecked 也会被调用。所以你可以使用这个钩子来关注最后一个已经创建的输入。

<div *ngFor="let foo of elements">
  <input 
    #input 
    class="input" 
    [value]="foo.value"
  />
</div>
<button (click)="addElement()"></button>
@ViewChildren('input') inputs: QueryList<ElementRef>;

addElement() {
  this.elements.push({ value: 'new' });
}

ngAfterViewChecked() {
  this.inputs.last.nativeElement.focus();
}

否则你可以自己检查,不要依赖生命周期钩子,因为ngAfterViewChecked中的这个逻辑可能会影响你应用程序中的smth:

@ViewChildren('input') inputs: QueryList<ElementRef>;

constructor(private changeDetectorRef: ChangeDetectorRef) {
}

addElement() {
  this.elements.push({ value: 'new' });
  /* run change detection manually to create input element and have access to the last input */
  this.changeDetectorRef.detectChanges();
  this.inputs.last.nativeElement.focus();
}

https://angular.io/api/core/ViewChildren#description

https://angular.io/guide/lifecycle-hooks#lifecycle-event-sequence

您可以通过跟踪 div 的子组件中的任何更改来实现此目标。每次创建或刷新 div 时,将焦点设置在最后一个 input 子项上。

为此,您需要:

  • 一个指令将一些行为逻辑添加到 div 容器
  • 一个指令来标识每个 input 标签

这样,您的模板可以是:

<div track-focus *ngFor="let foo of elements">
  <input auto-focus ... />
</div>
<button (click)="addElement()">Add</button>
@Directive({
  selector: 'input[auto-focus]',
})
export class InputAutoFocusDirective {}
@Directive({
  selector: '[track-focus]',
})
export class TrackFocusDirective implements AfterContentInit {
  @ContentChildren(InputAutofocusDirective, {
    read: ElementRef,
  })
  children: QueryList<InputAutoFocusDirective>;

  ngAfterContentInit() {
    const input = (this.children.last as ElementRef)
      .nativeElement as HTMLElement;
    if (input) {
      input.focus();
    }
  }
}

在这里,您有一个可重复使用的解决方案。

Angular 可以使用 ViewChildren.

访问 ngFor 中的元素

您可以通过以下方式更新您的 addElement 函数以获得结果。

@ViewChildren("row") rows;
public addElement() {
    this.elements.push({ value: "new" });
    setTimeout(() => {
      this.rows.last.nativeElement.focus();
    }, 0);
  }

并如下更新您的 ngFor 模板:

<div *ngFor="let foo of elements">
  <input #row class="input" value="{{ foo.value }}" />
</div>

这里是 stackblitz 的例子:https://stackblitz.com/edit/angular-ivy-xtkvaz