You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular NgbModal确认弹窗问题:父弹窗未关闭、重复触发失效

问题原因分析

  1. 点击「Yes, Close」仅关闭确认弹窗:
    原代码中编辑弹窗的close()方法仅通过save.emit(null)通知父组件,但如果父组件未正确监听该事件并执行关闭逻辑,编辑弹窗无法自动关闭。同时,编辑弹窗组件未注入NgbActiveModal,无法直接控制自身的关闭行为。

  2. 取消确认后再次点击X不触发确认弹窗:
    原代码依赖单一布尔值hasUnsavedChanges标记状态,若取消确认后该状态被意外重置(比如组件内其他逻辑影响),或初始状态判断逻辑不严谨,会导致后续关闭操作无法检测到未保存更改。此外,未使用NgbModal官方的关闭拦截机制,可能存在事件处理遗漏。

解决方案

采用NgbModal官方推荐的beforeDismiss钩子拦截关闭事件,结合初始值对比判断未保存更改,同时让编辑弹窗通过NgbActiveModal直接控制自身关闭。

修改后的确认弹窗组件(ConfirmModalComponent)

简化组件逻辑,利用NgbActiveModal的原生结果返回机制替代自定义Output:

import { Component, Input } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-confirm-modal',
  templateUrl: './confirm-modal.component.html',
  styleUrls: ['./confirm-modal.component.scss']
})
export class ConfirmModalComponent {
  @Input() title: string = 'Confirmation';
  @Input() message: string = 'Are you sure you want to proceed?';
  @Input() confirmButtonText: string = 'Yes';
  @Input() cancelButtonText: string = 'No';

  constructor(public activeModal: NgbActiveModal) {}

  confirm() {
    this.activeModal.close(true);
  }

  cancel() {
    this.activeModal.dismiss(false);
  }
}

修改后的编辑弹窗组件(FeatureValuesEditDialogComponent)

添加NgbActiveModal注入,使用beforeDismiss拦截关闭,通过初始值对比判断未保存更改:

import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
import { ConfirmModalComponent } from '../../../shared/components/confirm-modal/confirm-modal.component';

@Component({
  selector: 'app-feature-values-edit-dialog',
  templateUrl: './feature-values-edit-dialog.component.html',
  styleUrls: ['./feature-values-edit-dialog.component.scss']
})
export class FeatureValuesEditDialogComponent implements OnInit {
  @Input() value: any = { level: '', levelId: '', levelValue: '', begins: '', ends: '' };
  @Output() save = new EventEmitter<any>();

  hasUnsavedChanges: boolean = false;
  private initialValue: any;

  constructor(
    private modalService: NgbModal,
    public activeModal: NgbActiveModal
  ) {}

  ngOnInit(): void {
    // 保存初始值,用于后续对比判断未保存更改
    this.initialValue = JSON.parse(JSON.stringify(this.value));
    
    // 拦截弹窗关闭事件(点击X、ESC等场景)
    this.activeModal.beforeDismiss().subscribe(() => {
      return this.handleCloseConfirmation();
    });
  }

  // 监听值变化,更新未保存状态
  onValueChanged() {
    this.hasUnsavedChanges = JSON.stringify(this.value) !== JSON.stringify(this.initialValue);
  }

  /**
   * 处理关闭确认逻辑,返回Promise控制是否允许关闭
   */
  private handleCloseConfirmation(): Promise<boolean> {
    if (!this.hasUnsavedChanges) {
      return Promise.resolve(true);
    }

    const modalRef = this.modalService.open(ConfirmModalComponent, {
      centered: true,
      backdrop: 'static',
      keyboard: false
    });

    modalRef.componentInstance.title = 'Unsaved Changes';
    modalRef.componentInstance.message = 'You have unsaved changes. Are you sure you want to close?';
    modalRef.componentInstance.confirmButtonText = 'Yes, Close';
    modalRef.componentInstance.cancelButtonText = 'No, Keep Editing';

    return modalRef.result
      .then(() => {
        // 用户确认关闭:重置状态并允许关闭
        this.hasUnsavedChanges = false;
        return true;
      })
      .catch(() => {
        // 用户取消关闭:阻止编辑弹窗关闭
        return false;
      });
  }

  // 保存按钮逻辑
  onSave() {
    this.save.emit(this.value);
    this.hasUnsavedChanges = false;
    this.activeModal.close();
  }
}

模板补充

在编辑弹窗的所有输入控件上绑定值变化事件,确保hasUnsavedChanges状态及时更新:

<input [(ngModel)]="value.level" (ngModelChange)="onValueChanged()">
<input [(ngModel)]="value.levelId" (ngModelChange)="onValueChanged()">
<!-- 其他输入控件同理 -->

更优方案说明

  1. 官方拦截机制:使用beforeDismiss钩子覆盖所有弹窗关闭场景(点击X、ESC、调用dismiss方法等),避免遗漏。
  2. 可靠状态判断:通过初始值与当前值的JSON对比,替代单一布尔值标记,避免状态错误。
  3. 简化事件处理:利用modalRef.result订阅确认弹窗结果,无需自定义Output事件,符合NgbModal设计规范。

内容的提问来源于stack exchange,提问作者san

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 01:24:56