Angular动态生成输入框获取MatAutocompleteTrigger实例问题及解决
解决动态生成Angular Material输入框的自动补全面板关闭问题
我之前也碰到过一模一样的场景——动态表格里的带自动补全输入框,按回车切换焦点时,自动补全的面板总是关不掉,要么只能操作第一个输入框的面板。下面说说问题的根源和最终的解决办法:
问题场景
我用*ngFor动态生成了行列可变的表格,每个单元格都是带mat-autocomplete的输入框,核心模板代码如下:
<tr *ngFor="let row of rows; let rowIdx = index"> <td *ngFor="let col of columns; let colIdx = index"> <mat-form-field class="example-full-width"> <input #inputs #test type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto" (keyup.enter)="shiftFocusEnter(rowIdx, colIdx)"> <mat-autocomplete #auto="matAutocomplete"> <mat-option *ngFor="let option of filteredOptions | async" [value]="option"> {{ option }} </mat-option> </mat-autocomplete> </mat-form-field> </td> </tr>
需求很明确:按下回车键切换到下一个输入框时,要关闭所有打开的自动补全面板。但一开始的尝试遇到了两个问题:
- 用
@ViewChild('test', { read: MatAutocompleteTrigger }) test: MatAutocompleteTrigger;只能拿到第一个输入框的触发器,导致只有第一个面板能被关闭; - 改用
@ViewChildren后,直接调用this.test.closePanel()会报错this.test.closePanel is not a function。
问题原因
@ViewChild的特性就是只返回匹配选择器的第一个元素,所以没法处理批量的动态元素;@ViewChildren返回的是QueryList<MatAutocompleteTrigger>类型的集合对象,而不是单个触发器实例,集合本身没有closePanel()方法,必须遍历集合里的每个实例才能调用方法。
最终解决办法
首先在组件类中正确定义ViewChildren:
import { Component, ViewChildren, QueryList, ElementRef } from '@angular/core'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; // ...其他导入和组件装饰器 export class YourComponent { @ViewChildren('test', { read: MatAutocompleteTrigger }) autocompletes: QueryList<MatAutocompleteTrigger>; @ViewChildren('inputs') inputs: QueryList<ElementRef>; // ...你的rows、columns、myControl、filteredOptions等定义 shiftFocusEnter(rowIdx: number, colIdx: number) { console.log("Enter", rowIdx, colIdx); if(colIdx == 4 && rowIdx == 5) { console.log("Reached end of row"); } else { // 根据你的业务逻辑计算下一个可编辑列的索引 colIdx = colIdx + this.columns.findIndex(c => c.editable); this.focusInput(rowIdx, colIdx); // 遍历所有自动补全触发器,关闭面板 this.autocompletes.toArray().forEach(el => { el.closePanel(); }) } } focusInput(rowIdx: number, colIdx: number) { console.log("FOCUS ON", rowIdx, colIdx); let inputEls = this.inputs.toArray(); // 计算扁平化后的索引,定位到目标输入框 let flatIdx = (rowIdx * this.columns.length) + colIdx; inputEls[flatIdx].nativeElement.focus(); } }
关键细节说明
this.autocompletes.toArray():把QueryList转换成普通数组,这样就能遍历每个MatAutocompleteTrigger实例;- 遍历调用
closePanel():确保所有打开的自动补全面板都被强制关闭; - 焦点切换逻辑:通过计算行列对应的扁平化索引,从
inputs集合中找到目标输入框,调用nativeElement.focus()完成焦点切换。
内容的提问来源于stack exchange,提问作者Ekos
相关产品推荐
相关产品推荐

