Angular 15+中mat-selection-list事件触发后代码阻塞至失焦问题
Angular 15+ mat-selection-list 焦点状态下代码阻塞问题
问题场景
在Angular 15+项目中使用mat-selection-list实现单选列表,初始加载时子组件能正常接收输入值,但切换列表项时,ngModelChange事件触发正常,但代码卡在实例化IdNotify的位置,直到点击列表外失焦后才继续执行,子组件才收到更新。绑定click事件也无法解决该问题。
相关代码
模板代码(mat-selection-list)
<mat-selection-list #contactsList [(ngModel)]="selectedAssociation" (ngModelChange)="onAssociateSelectionChanged($event)" [multiple]=false hideSingleSelectionIndicator=true> <mat-list-option *ngFor="let contact of model_list?.list; index as i; first as isFirst" color="primary" [value]="contact" [selected]="isFirst" > <mat-icon matListItemIcon class="mat-list-item-icon">{{contactClassIcons[contact.contact_types_id!]}}</mat-icon> <div matListItemTitle class="profile-line-primary"> {{contact.contact_typeclass_title}} </div> <div matListItemLine class="profile-line-secondary"> {{contact.contact_info}} </div> </mat-list-option> </mat-selection-list>
父组件TS代码
idNotify: IdNotify; onAssociateSelectionChanged(selectedAssociationList:ProfileContact[] | ProfileAcademy[]){ console.log('SelectedContact:',selectedAssociationList) let selectedAssociationModel : ProfileContact | ProfileAcademy = (selectedAssociationList.length == 0 && this.model_list!.list.length >0)? this.model_list?.list[0] : selectedAssociationList[0]; this.idNotify = // 准备向下游传递数据 new IdNotify() // <----**代码卡在此处** console.log('sending downstream ', selectedAssociationModel) }
子组件模板引用
<profile-contact-edit [idNotify]="idNotify"></profile-contact-edit>
子组件TS代码(setter接收输入)
@Input() set idNotify(id: IdNotify) { console.log(`id ${JSON.stringify(id)} `) }
解决方案
1. 利用NgZone强制触发同步执行
Angular在组件焦点状态下可能延迟变更检测,通过NgZone的run方法可强制代码同步执行,确保数据及时更新:
import { NgZone } from '@angular/core'; // 构造函数注入NgZone constructor(private ngZone: NgZone) {} onAssociateSelectionChanged(selectedAssociationList:ProfileContact[] | ProfileAcademy[]){ console.log('SelectedContact:',selectedAssociationList) let selectedAssociationModel : ProfileContact | ProfileAcademy = (selectedAssociationList.length == 0 && this.model_list!.list.length >0)? this.model_list?.list[0] : selectedAssociationList[0]; this.ngZone.run(() => { // 补充传入selectedAssociationModel的必要数据到IdNotify this.idNotify = new IdNotify(selectedAssociationModel.id); console.log('sending downstream ', selectedAssociationModel) }); }
2. 手动触发变更检测
若NgZone方法无效,可注入ChangeDetectorRef手动标记并触发变更检测:
import { ChangeDetectorRef } from '@angular/core'; // 构造函数注入ChangeDetectorRef constructor(private cdr: ChangeDetectorRef) {} onAssociateSelectionChanged(selectedAssociationList:ProfileContact[] | ProfileAcademy[]){ console.log('SelectedContact:',selectedAssociationList) let selectedAssociationModel : ProfileContact | ProfileAcademy = (selectedAssociationList.length == 0 && this.model_list!.list.length >0)? this.model_list?.list[0] : selectedAssociationList[0]; this.idNotify = new IdNotify(selectedAssociationModel.id); console.log('sending downstream ', selectedAssociationModel); // 手动触发变更检测 this.cdr.markForCheck(); // 或使用detectChanges()立即执行检测 // this.cdr.detectChanges(); }
3. 排查IdNotify构造逻辑
检查IdNotify的构造函数是否存在同步耗时操作或隐性阻塞逻辑,如果有,将其改为异步处理避免主线程阻塞:
// 父组件中 async onAssociateSelectionChanged(selectedAssociationList:ProfileContact[] | ProfileAcademy[]){ console.log('SelectedContact:',selectedAssociationList) let selectedAssociationModel : ProfileContact | ProfileAcademy = (selectedAssociationList.length == 0 && this.model_list!.list.length >0)? this.model_list?.list[0] : selectedAssociationList[0]; this.idNotify = await this.createIdNotify(selectedAssociationModel); console.log('sending downstream ', selectedAssociationModel); this.cdr.detectChanges(); } private async createIdNotify(model: ProfileContact | ProfileAcademy): Promise<IdNotify> { const notify = new IdNotify(); // 替换为实际异步初始化逻辑 // notify.data = await this.someService.fetchExtraData(model.id); return notify; }
说明
问题根源大概率是Angular在组件焦点状态下延迟处理变更检测队列,或IdNotify实例化过程中存在隐性阻塞。通过上述方法可确保数据更新及时同步到子组件,无需等待列表失焦。
内容的提问来源于stack exchange,提问作者Timy Shark
相关产品推荐
相关产品推荐

