Angular 5 Bootstrap对话框重复触发回调问题求助
解决ngx-bootstrap Modal onHide事件重复订阅导致回调多次执行的问题
哈哈,这个问题我之前也踩过类似的坑!核心原因很明确:你每次调用openConfirmComponent时,都会给this.modalService.onHide新增一个订阅,但之前的订阅从来没有被取消,所以每打开一次弹窗,就会多一个“潜伏”的监听,关闭时这些订阅就会依次触发,导致回调执行次数随打开次数递增。
下面给你几种靠谱的解决思路,包括你已经发现的方法,还有更符合Angular最佳实践的方式:
方案1:把订阅移到构造函数(你的解决方法,简单直接)
把onHide的订阅逻辑放到ModalService的构造函数里,只订阅一次,这样就不会重复叠加了。不过要记得在回调执行后清空当前的回调引用,避免残留影响下一次弹窗:
@Injectable() export class ModalService { bsModalRef: BsModalRef; // 用变量暂存当前弹窗的回调 private currentCallback: ((result: boolean) => void) | null = null; constructor(private modalService: BsModalService) { // 只在初始化时订阅一次全局onHide事件 this.modalService.onHide.subscribe(()=>{ if(this.currentCallback && this.bsModalRef?.content) { console.log('calling callback from confirm'); this.currentCallback(this.bsModalRef.content.result); // 执行完回调就清空,避免影响下一次弹窗 this.currentCallback = null; } }); } openConfirmComponent(title:string, message:string, callback: (result: boolean) => void) { const initialState = { message:message, title:title, result:false }; // 保存当前弹窗的回调 this.currentCallback = callback; this.bsModalRef = this.modalService.show(ConfirmComponent, {initialState}); } }
方案2:手动取消订阅(每次弹窗关闭后清理)
每次订阅onHide时把订阅对象存下来,回调执行完立刻取消订阅,这样就不会留下多余的监听:
openConfirmComponent(title:string, message:string, callback: (result: boolean) => void) { const initialState = { message:message, title:title, result:false }; this.bsModalRef = this.modalService.show(ConfirmComponent, {initialState}); // 保存订阅实例 const hideSubscription = this.modalService.onHide.subscribe(()=>{ console.log('calling callback from confirm'); callback(this.bsModalRef.content.result); // 执行完就取消订阅,清理监听 hideSubscription.unsubscribe(); }); }
方案3:用takeUntil统一管理订阅(Angular推荐方式)
如果你的ModalService是组件级注入的(有生命周期),可以用RxJS的takeUntil操作符配合一个Subject来统一清理所有订阅,避免内存泄漏:
import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @Injectable() export class ModalService { bsModalRef: BsModalRef; // 用于通知订阅停止的Subject private destroy$ = new Subject<void>(); constructor(private modalService: BsModalService) {} openConfirmComponent(title:string, message:string, callback: (result: boolean) => void) { const initialState = { message:message, title:title, result:false }; this.bsModalRef = this.modalService.show(ConfirmComponent, {initialState}); this.modalService.onHide .pipe(takeUntil(this.destroy$)) // 当destroy$发出值时自动取消订阅 .subscribe(()=>{ console.log('calling callback from confirm'); callback(this.bsModalRef.content.result); }); } // 当Service销毁时调用,清理所有订阅 ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } }
方案4:订阅当前弹窗的onHidden事件(更精准)
其实BsModalRef实例本身就提供了onHiddenObservable,它只针对当前打开的弹窗触发关闭事件,完全不会和全局的modalService.onHide混淆,这应该是最精准的方式:
openConfirmComponent(title:string, message:string, callback: (result: boolean) => void) { const initialState = { message:message, title:title, result:false }; this.bsModalRef = this.modalService.show(ConfirmComponent, {initialState}); // 只监听当前弹窗的关闭事件 this.bsModalRef.onHidden.subscribe(()=>{ console.log('calling callback from confirm'); callback(this.bsModalRef.content.result); }); }
小提示
- 尽量别用
var that = this这种写法啦,箭头函数里的this会自动绑定外层上下文,直接用this就可以了。 - 给回调函数加上类型定义,比如
callback: (result: boolean) => void,能让代码更严谨,IDE也能给你更好的提示~
内容的提问来源于stack exchange,提问作者Oo-_-oO
相关产品推荐
相关产品推荐

