为何Angular中用函数返回数组时*ngFor会导致输入框失焦?
问题原因分析
这个问题的核心在于Angular的变更检测机制以及*ngFor的默认追踪逻辑:
- 函数返回新数组引用:每次Angular触发变更检测(比如输入框获焦、按键事件后),都会执行模板中的
testValues()函数。该函数每次调用都会返回一个全新的数组对象([{a:0}, {a:1}]是每次调用时重新创建的新引用)。 *ngFor的默认追踪规则:*ngFor默认通过对象引用判断列表项是否变化。当数组引用改变时,Angular会认为所有列表项都是新元素,于是销毁原有的DOM节点(包括输入框)并重新创建。这就导致输入框刚获焦就被重建,自然无法正常输入。
而直接在模板中写字面量数组[{a:0}, {a:1}]时,这个数组的引用在组件生命周期内是固定不变的,*ngFor不会触发DOM重建,输入框就能正常工作。
解决方案
方案1:将数组存储为组件属性
把数组提前初始化到组件的属性中,避免在模板中调用函数生成新数组:
组件代码修改:
export class AppComponent { // 提前初始化数组,保持引用稳定 testValues = [{ a: 0 }, { a: 1 }]; testFocus($event: FocusEvent) { console.log('focus ', $event); } }
模板修改:
<div *ngFor="let d of testValues"> <input (focus)="testFocus($event)" type="text" /> </div>
方案2:使用trackBy自定义追踪逻辑
如果必须通过函数动态生成数组(比如数组内容依赖其他变量),可以通过trackBy告诉Angular如何识别唯一列表项,避免不必要的DOM重建:
组件代码添加trackBy函数:
export class AppComponent { testValues() { return [{ a: 0 }, { a: 1 }]; } testFocus($event: FocusEvent) { console.log('focus ', $event); } // 自定义追踪函数,返回每个项的唯一标识 trackByItemId(index: number, item: any): number { return item.a; // 用item的a属性作为唯一标识,也可以使用index } }
模板修改:
<div *ngFor="let d of testValues(); trackBy: trackByItemId"> <input (focus)="testFocus($event)" type="text" /> </div>
内容的提问来源于stack exchange,提问作者MKaz
相关产品推荐
相关产品推荐

