Angular中*ngFor渲染项内调用NgbModal的open()方法模态框无法正常显示
问题原因分析
- 问题核心是Angular NgZone运行上下文丢失,导致NgbModal触发的组件渲染流程没有走完完整的变更检测周期
- 从
*ngFor内部触发点击时,事件回调没有进入Angular的变更检测上下文,因此模态框DOM插入后没有触发后续的样式绑定和子组件生命周期执行,就出现了只有构造函数日志、没有ngAfterViewInit日志、缺少show类的问题 - 外部按钮触发时因为上下文正常,所以运行逻辑完全正常
解决方案
方案1:在模态框服务内强制指定运行上下文(最推荐,通用且不影响业务逻辑)
修改testAlert.service.ts代码如下:
import { Injectable, NgZone } from '@angular/core'; import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap'; import { ConnectionModalComponent } from '../../components/shared'; @Injectable() export class TestAlertService { constructor( private modalService: NgbModal, // 注入NgZone private ngZone: NgZone ) {} open() { // 将模态框打开逻辑包裹在ngZone.run中,强制进入Angular变更检测上下文 this.ngZone.run(() => { this.modalService.open(ConnectionModalComponent, { ariaLabelledBy: 'modal-basic-title' }).result.then( (result) => { console.log(`Closed with: ${result}`); }, (reason) => { console.log(`Dismissed ${this.getDismissReason(reason)}`); } ); }) } private getDismissReason(reason: any): string { if (reason === ModalDismissReasons.ESC) { return 'by pressing ESC'; } else if (reason === ModalDismissReasons.BACKDROP_CLICK) { return 'by clicking on a backdrop'; } else { return `with: ${reason}`; } } }
方案2:修正列表数据的赋值上下文
如果你的items数组是在非Angular上下文(比如原生WebSocket回调、未被Angular封装的第三方事件回调、原生定时器)中赋值的,将赋值操作也包裹在NgZone.run中即可从根源解决问题,无需修改模态框服务代码。
快速验证方案
你可以先修改testComponent.component.ts的触发代码做快速验证,确认问题根因:
public setSelected(item: ItemViewModel): void { // setTimeout会自动进入Angular上下文,延迟0秒不影响用户感知 setTimeout(() => { this.testAlertService.open(); }, 0) }
如果修改后模态框可以正常弹出,即可确认是NgZone上下文丢失的问题,再选用上面的正式方案修复即可。
内容的提问来源于stack exchange,提问作者ericegonz
相关产品推荐
相关产品推荐

