Angular Material datepicker与本地ChangeDetection冲突致焦点丢失问题求解
Angular OnPush变更检测与Material Datepicker冲突解决方案
问题本质:宿主组件开启
ChangeDetectionStrategy.OnPush后,Material Datepicker挂载的CDK浮层内部状态更新(月份/年份切换、焦点移动)无法主动触发宿主组件的变更检测,导致视图更新不同步,出现焦点丢失、选中状态失效的问题。
以下两种方案均可在保留OnPush策略、不影响第三方地图性能的前提下解决冲突:
方法一:事件触发手动标记变更检测
在宿主组件中注入ChangeDetectorRef,监听Datepicker的状态变更事件,触发时手动标记组件为待检查状态即可,示例代码如下:
组件TS代码:import { ChangeDetectorRef, ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'app-map-component', templateUrl: './map-component.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class MapComponent { constructor(private cdr: ChangeDetectorRef) {} syncDatepickerState() { // 标记组件为脏,等待下一次变更检测更新视图 this.cdr.markForCheck(); } }模板代码:
<input [matDatepicker]="picker" /> <mat-datepicker #picker (monthSelected)="syncDatepickerState()" (yearSelected)="syncDatepickerState()" (opened)="syncDatepickerState()" (keydown)="syncDatepickerState()" ></mat-datepicker>该方案侵入性极低,仅在Datepicker状态变更时触发一次变更检测,完全不影响第三方地图的性能优化效果。
方法二:封装通用复用指令
如果你项目中多处OnPush组件都用到了Material Datepicker,可以封装一个通用指令自动完成变更检测同步,避免每个组件重复写事件绑定:
指令代码:import { Directive, HostListener } from '@angular/core'; import { ChangeDetectorRef } from '@angular/core'; import { MatDatepicker } from '@angular/material/datepicker'; @Directive({ selector: 'mat-datepicker[cdSync]' }) export class DatepickerCdSyncDirective { constructor( private cdr: ChangeDetectorRef, private datepicker: MatDatepicker<Date> ) {} @HostListener('monthSelected') @HostListener('yearSelected') @HostListener('opened') @HostListener('keydown') syncState() { this.cdr.markForCheck(); } }使用时只需要在mat-datepicker标签上加
cdSync属性即可:<mat-datepicker #picker cdSync></mat-datepicker>
内容的提问来源于stack exchange,提问作者Exitl0l
相关产品推荐
相关产品推荐

