Angular点击事件异常:传入对象数组时需双击触发
问题分析与解决方案
问题症状
当向mdj-select-list组件传入对象类型数组时,需要双击.option元素才能触发selectOption()函数;但传入字符串类型数组时,单次点击即可正常触发。
根本原因
这个问题的核心在于Angular *ngFor的默认跟踪机制:
- 对于字符串这类值类型,Angular会直接对比值是否相等,只要内容不变,不会重新渲染列表项。
- 对于对象类型,Angular默认对比的是对象引用。如果父组件每次传递
options时都是新建的数组(比如通过map、扩展运算符等方式生成新数组),即使数组内对象的内容没变,Angular也会判定为列表项需要重新渲染——第一次点击时,目标元素刚好被销毁并重建,导致点击事件丢失,第二次点击的是新渲染完成的元素,事件才能正常触发。
解决方案
给*ngFor添加trackBy函数,让Angular根据对象的唯一标识(比如value字段)来跟踪列表项,避免不必要的重渲染。
1. 修改组件类代码
在SelectListComponent中添加trackByOption函数:
import { Component, ElementRef, HostListener, Input, Output, ViewChild, } from '@angular/core'; @Component({ selector: 'mdj-select-list', templateUrl: './select-list.component.html', styleUrls: ['./select-list.component.scss'], }) export class SelectListComponent { @Input() options: { label: string, value: string }[] = []; selectedOption: string; selectOption() { console.log('click'); } // 添加trackBy函数,根据value字段跟踪选项 trackByOption(index: number, option: { label: string, value: string }): string { return option.value; } }
2. 修改模板代码
在*ngFor指令中绑定trackBy:
<div class="options-wrapper"> <div *ngFor="let option of options; trackBy: trackByOption" class="option" (click)="selectOption()"> {{ option.label }} </div> </div>
额外优化
另外,建议在selectOption中传入当前选中的选项,这样能获取到点击的具体数据:
<div *ngFor="let option of options; trackBy: trackByOption" class="option" (click)="selectOption(option)"> {{ option.label }} </div>
selectOption(option: { label: string, value: string }) { this.selectedOption = option.value; console.log('选中选项:', option); }
内容的提问来源于stack exchange,提问作者Michael Kitchell
相关产品推荐
相关产品推荐

