什么是最好的angular 7 hook来处理ViewChildren

What is the best angular 7 hook to handle ViewChildren

我有一个 Angular 7 应用程序,我试图在其中处理 ngAfterViewChecked() 中的文本输入。

文本输入是矩阵树中的一个节点。它的可见性取决于 ngIf 条件。如果不满足该条件,我将改为显示跨度。本质上,如果用户双击树中的一个节点(一个 span 元素),它就变成了一个文本输入,以便用户可以编辑文本:

<mat-tree [dataSource]="nestedDataSource" [treeControl]="nestedTreeControl">
  <mat-tree-node *matTreeNodeDef="let node">
    <li>
      <span *ngIf="!node.isInput" (dblClick)="nodeDoubleClicked(node)">{{ node.name }}</span>
      <input *ngIf="node.isInput" #nodeNameInput type="text" [(ngModel)]="node.name" (blur)="doneEditting(node)" (keypress)="keyPressed($event, node)" />
    </li>
  </mat-tree-node>
  <mat-nested-tree-node *matTreeNodeDef="let node; when: hasNestedChild">
    <button mat-icon-button matTreeNodeToggle>
      <mat-icon>
        {{ nestedTreeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
      </mat-icon>
    </button>
    <span *ngIf="!node.isInput" (dblClick)="nodeDoubleClicked(node)">{{ node.name }}</span>
    <input *ngIf="node.isInput" #nodeNameInput type="text" [(ngModel)]="node.name" (blur)="doneEditting(node)" (keypress)="keyPressed($event, node)" />
    <ul [class.collapsed]="!nestedTreeControl.isExpanded(node)">
      <ng-container matTreeNodeOutlet></ng-container>
    </ul>
  </mat-nested-tree-node>
</mat-tree>

当用户双击一个节点时,我不仅希望它变成输入文本,我还希望它获得焦点和select里面的文本。为此,我必须获取本机元素并对其调用 .focus() 和 .select() 。为了获得本机元素,我必须使用 ViewChildren(其中输入标记有 #nodeNameInput,如您在上面的代码片段中所见)。最后,我需要连接到 ngAfterViewChecked() 以确保 ViewChildren 的 QueryList 已准备就绪。

组件代码如下:

@ViewChildren('nodeNameInput') nodeNameInputs: QueryList<ElementRef>;

...

ngAfterViewChecked() {
  if (this.nodeNameInputs && this.nodeNameInputs.length) {
    this.nodeNameInputs.first.nativeElement.focus();
    this.nodeNameInputs.first.nativeElement.select();
  }
}

我确保一次只能编辑一个节点,因此先使用而不是搜索 nodeNameInputs 来找到要聚焦的节点和 select 文本是安全的。

这似乎可行,但有一个问题。似乎对于每个击键,也会调用 ngAfterViewChecked() 。这意味着当用户编辑节点的文本时,每次击键都会重新select编辑。这会导致用户输入的文本在每次击键时被覆盖。

我有解决这个问题的方法:

ngAfterViewChecked() {
  if (this.nodeNameInputs && this.nodeNameInputs.length) {
    this.nodeNameInputs.first.nativeElement.focus();
    if (!this.keyStroked) {
      this.nodeNameInputs.first.nativeElement.select();
    }
  }
}

...其中 keyStroked 在 keyPressed 处理程序中设置并在模糊处理程序中设置为 false。

但我想知道是否有另一个挂钩可以可靠地用于聚焦输入和 select 它的文本,同时不响应击键。我选择 ngAfterViewChecked 是因为测试表明它是唯一一个每次都始终准备好 nodeNameInputs 的挂钩(即 this.nodeNameInputs.length 始终为 1)。但也许我错过了某些钩子。

我的解决方法似乎是一个 hack。你会如何解决这个问题?

创建焦点指令并将其放在您想要焦点的输入上,您将不必担心生命周期事件。

import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[focus]'
})
export class FocusDirective {

  constructor(elm: ElementRef) {
    elm.nativeElement.focus();
  }
}

并使用它

<input focus>

https://stackblitz.com/edit/angular-qnjw1s?file=src%2Fapp%2Fapp.component.html