当 ngIf 可能会删除它时,如何从打字稿中引用 Angular 组件?

Set*_*eth 5 typescript angular-material angular-components angular

我有一个使用MatInputAngular Material的模板。我试图从代码中访问这个组件,@ViewChild以便我可以以编程方式改变focused状态,但是MatInput当视图初始化时它不存在 - 它的存在是由*ngIf指令确定的。基本上,当视图加载时,有一个p带有一些文本的元素。如果用户单击该元素,它将被替换input为起始值是原始元素文本的 。当它失去焦点时,它会被保存并恢复为一个p元素。问题是当他们第一次单击文本进行更改时,input创建的文本没有焦点 - 他们必须再次单击它才能开始编辑。我希望他们能够单击一次并开始输入。

这是我的相关代码。

模板:

<mat-form-field *ngIf="selected; else staticText" class="full-width">
  <input matInput type="text" [(ngModel)]="text" (blur)="save()">
</mat-form-field>
<ng-template #staticText>
  <p class="selectable" (click)="select()">{{ text }}</p>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

打字稿:

import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
import { MatInput } from '@angular/material';
import { AfterViewInit } from '@angular/core/src/metadata/lifecycle_hooks';

@Component({
  selector: 'app-click-to-input',
  templateUrl: './click-to-input.component.html',
  styleUrls: ['./click-to-input.component.scss']
})
export class ClickToInputComponent implements AfterViewInit {
  @Input() text: string;
  @Output() saved = new EventEmitter<string>();
  @ViewChild(MatInput) input: MatInput;

  selected = false;

  ngAfterViewInit(): void {
    console.log(this.input); // shows undefined - no elements match the ViewChild selector at this point
  }

  save(): void {
    this.saved.emit(this.text);
    this.selected = false;
  }

  select(): void {
    this.selected = true;  // ngIf should now add the input to the template
    this.input.focus();    // but input is still undefined
  }
}
Run Code Online (Sandbox Code Playgroud)

从文档:

您可以使用 ViewChild 从视图 DOM 中获取与选择器匹配的第一个元素或指令。如果视图 DOM 发生变化,并且新的子级与选择器匹配,则该属性将更新。

是否*ngIf工作速度太慢,我试图this.input在属性更新之前过早访问?如果是这样,我怎么能等到*ngIf完成替换 DOM 然后访问MatInput? 或者有没有其他方法可以完全解决我没有看到的对焦问题?

Con*_*Fan 4

我在这个 stackblitz中重现了你的案例。设置后this.selected = true,Angular 必须执行更改检测才能显示mat-form-field元素,这通常会在当前执行周期之后发生。立即访问输入元素的一种方法是触发代码中的更改检测,例如ChangeDetector.detectChanges(有关其他技术,请参阅此答案):

import { Component, ChangeDetectorRef, ViewChild } from '@angular/core';
import { MatInput } from '@angular/material';

@Component({
  ...
})
export class FormFieldPrefixSuffixExample {

  @ViewChild(MatInput) input: MatInput;

  text = "Hello world!"
  selected = false;

  constructor(private changeDetector: ChangeDetectorRef) {
  }

  select(): void {
    this.selected = true;
    this.changeDetector.detectChanges();
    this.input.focus();
  }  
}
Run Code Online (Sandbox Code Playgroud)

kiranghule27 建议的另一种解决方法是this.input.focus()通过使其异步来延迟调用:

  select(): void {
    this.selected = true;
    setTimeout(() => {
      this.input.focus();
    }, 0);
  }  
Run Code Online (Sandbox Code Playgroud)