Angular中mat-select的compareWith早于@Input传值触发的解决办法
问题描述
我编写了如下compareObjects方法作为mat-select的compareWith函数:
compareObjects() { return (o1: any, o2: any) => { if (this.displayKey) { if (o1 && o2) { return o1[this.displayKey] === o2[this.displayKey]; } } return o1 === o2; }; }
其中displayKey是通过@Input传入的属性。当前遇到的问题是:mat-select的compareWith触发时机早于@Input属性的赋值,导致this.displayKey始终为undefined。尝试过setTimeout延迟对比逻辑、在ngAfterViewCheck中处理数据,但都无法解决——虽然这些生命周期钩子能获取到传入的displayKey,但mat-select的对比逻辑依然不生效。而在ngOnInit、ngOnChanges等钩子中能正常打印this.displayKey,唯独compareWith的触发时机更早。
组件模板中的mat-select配置如下:
<mat-select #input [(ngModel)]="value" type="text" matInput [errorStateMatcher]="matcher" [placeholder]="label" [required]="required" (selectionChange)="itemSelected()" (keydown.arrowDown)="selectNextOption()" (openedChange)="searchSelectInput.focus()" [compareWith]="compareObjects"> ... </mat-select>
解决方案
方案1:将对比函数改为箭头函数绑定this
直接把compareObjects定义为箭头函数,确保每次调用时都能获取到最新的displayKey值:
compareObjects = (o1: any, o2: any): boolean => { if (this.displayKey) { if (o1 && o2) { return o1[this.displayKey] === o2[this.displayKey]; } } return o1 === o2; };
模板中保持[compareWith]="compareObjects"即可。箭头函数会绑定当前组件实例的this,每次触发对比时都会读取最新的displayKey,不会被初始的undefined值固化。
方案2:监听displayKey变化,动态生成对比函数
通过@Input的setter监听displayKey的变化,每次变化时重新创建对比函数,确保mat-select使用最新的逻辑:
private _displayKey: string; @Input() set displayKey(key: string) { this._displayKey = key; // 每次displayKey更新时重新生成对比函数 this.compareObjects = this.createCompareFn(); } get displayKey(): string { return this._displayKey; } compareObjects: (o1: any, o2: any) => boolean; ngOnInit() { // 初始化时生成默认对比函数 this.compareObjects = this.createCompareFn(); } private createCompareFn() { return (o1: any, o2: any) => { if (this._displayKey) { if (o1 && o2) { return o1[this._displayKey] === o2[this._displayKey]; } } return o1 === o2; }; }
模板中依然使用[compareWith]="compareObjects",当displayKey传入或更新时,对比函数会同步更新,mat-select会自动使用新的函数进行选项匹配。
方案3:手动触发变更检测
如果上述方案不适用,可以在ngOnChanges中监听displayKey的变化,调用ChangeDetectorRef强制触发组件变更检测,确保mat-select更新对比逻辑:
import { ChangeDetectorRef, SimpleChanges } from '@angular/core'; constructor(private cdr: ChangeDetectorRef) {} ngOnChanges(changes: SimpleChanges) { if (changes['displayKey']) { // 触发变更检测,让mat-select感知到对比逻辑的变化 this.cdr.detectChanges(); } }
同时确保compareObjects能获取到最新的displayKey(比如使用方案1的箭头函数形式)。
原因分析
原代码中compareObjects()在组件初始化阶段就被调用,返回的闭包捕获了当时的this.displayKey(此时@Input属性尚未赋值,值为undefined)。后续即使displayKey更新,闭包里的旧值不会同步更新,导致对比逻辑始终无法正确使用传入的displayKey。
内容的提问来源于stack exchange,提问作者Arter

