Какой самый лучший угловой крючок 7 для обработки ViewChildren - PullRequest
0 голосов
/ 04 июля 2019

У меня есть приложение 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>

Когда пользователь дважды щелкает узел, я хочу, чтобы он не только превратился во входной текст, я хочу, чтобы он получил фокус и выделил текст внутри. Для этого мне нужно получить нативный элемент и вызвать .focus () и .select () для него. Чтобы получить нативный элемент, я должен использовать ViewChildren (где ввод помечен #nodeNameInput, как вы можете видеть в фрагменте кода выше). И, наконец, мне нужно подключиться к ngAfterViewChecked (), чтобы убедиться, что QueryList для ViewChildren готов.

Вот код для компонента:

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

...

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

Я удостоверился, что за один раз редактируется только один узел, поэтому безопаснее использовать сначала, а не искать в nodeNameInputs, чтобы найти тот, который нужно сфокусировать и выделить текст.

Кажется, это работает, но есть проблема. Кажется, что для каждого нажатия клавиши также вызывается ngAfterViewChecked (). Это означает, что, поскольку пользователь редактирует текст для узла, он переизбирается для каждого нажатия клавиши. Это приводит к тому, что вводимый пользователем текст перезаписывается при каждом нажатии клавиши.

У меня есть решение этой проблемы:

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

... где keyStroked устанавливается в обработчике keyPressed и устанавливается в false в обработчике размытия.

Но мне интересно, есть ли другой хук, который можно надежно использовать для фокусировки ввода и выделения его текста, не реагируя на нажатия клавиш. Я выбрал ngAfterViewChecked, потому что тест показал, что это был единственный хук, в котором nodeNameInputs всегда был готов каждый раз (то есть this.nodeNameInputs.length всегда был 1). Но, может быть, я пропустил некоторые крючки.

Мой обходной путь кажется хаком. Как бы вы решили эту проблему?

1 Ответ

1 голос
/ 04 июля 2019

Создайте директиву focus и поместите ее на ввод, который вы хотите сфокусировать, вам не придется беспокоиться о событиях жизненного цикла.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...