如何处理窗口resize页面缩放触发的ExpressionChangedAfterItHasBeenChecked错误
问题解决思路
错误原因
该报错是Angular开发模式下的变更检测校验触发:你在ngAfterViewInit生命周期中手动触发了resize事件,回调函数直接修改了targetScale、targetTranslate这两个已完成模板绑定校验的属性,导致变更检测前后的属性值不一致,触发了异常。
解决方案
方案1:手动触发变更检测(最推荐)
注入Angular内置的ChangeDetectorRef服务,在属性修改完成后手动触发一轮变更检测,同步组件类和模板的属性值即可解决问题。同时可以给resize事件增加防抖逻辑,避免窗口拖动时频繁计算导致性能损耗。
修改后的核心代码如下:
import { Component, ViewChild, ElementRef, HostListener, AfterViewInit, ChangeDetectorRef } from '@angular/core'; export class NotificationsComponent implements AfterViewInit { @ViewChild('vpWidth') vpWidthElement: ElementRef<HTMLElement>; @ViewChild('vpHeight') vpHeightElement: ElementRef<HTMLElement>; @ViewChild('scaledElement') scaledElement: ElementRef<HTMLElement>; vpWidth: number; vpHeight: number; scalable: {height: number, width: number}; targetScale: {X: number, Y: number}; targetTranslate: {X: number, Y: number}; // 注入变更检测服务 constructor(private cdr: ChangeDetectorRef) { this.scalable = { width: 0, height: 0 }; // 初始设置默认缩放为1,避免页面刚加载时元素显示异常 this.targetScale = { X: 1, Y: 1 }; this.targetTranslate = { X: 0, Y: 0 }; } @HostListener('window:resize', ['$event']) onResize(event) { this.setScalingValues( this.scaledElement.nativeElement.offsetWidth, this.scaledElement.nativeElement.offsetHeight, this.vpWidthElement.nativeElement.offsetWidth, this.vpHeightElement.nativeElement.offsetHeight ); } ngAfterViewInit(): void { window.dispatchEvent(new Event('resize')); } setScalingValues(scalableWidth: number, scalableHeight: number, vpWidth: number, vpHeight: number) { this.scalable.width = scalableWidth; this.scalable.height = scalableHeight; this.vpWidth = vpWidth; this.vpHeight = vpHeight; this.targetScale.X = this.vpWidth / this.scalable.width; this.targetScale.Y = this.vpHeight / this.scalable.height; this.targetScale.X = Math.min(this.targetScale.X, this.targetScale.Y); this.targetScale.Y = this.targetScale.X; this.targetTranslate.X = Math.round((this.vpWidth - (this.scalable.width * this.targetScale.X)) / 2); this.targetTranslate.Y = Math.round((this.vpHeight - (this.scalable.height * this.targetScale.Y)) / 2); // 手动触发变更检测 this.cdr.detectChanges(); } }
方案2:延后触发resize事件(改动最小)
不需要修改其他逻辑,仅需要将ngAfterViewInit中的resize触发逻辑放到下一个事件循环执行,即可避开当前变更检测周期的校验:
ngAfterViewInit(): void { setTimeout(() => { window.dispatchEvent(new Event('resize')); }, 0); }
内容的提问来源于stack exchange,提问作者morgred
相关产品推荐
相关产品推荐

