diff --git a/components/core/config/config.ts b/components/core/config/config.ts
index fa0414c29b2..5208d3ff22d 100644
--- a/components/core/config/config.ts
+++ b/components/core/config/config.ts
@@ -171,6 +171,7 @@ export interface MessageConfig {
export interface ModalConfig {
nzMask?: boolean;
nzMaskClosable?: boolean;
+ nzCloseOnNavigation?: boolean;
}
export interface NotificationConfig extends MessageConfig {
diff --git a/components/modal/doc/index.en-US.md b/components/modal/doc/index.en-US.md
index b91e94f4866..b892ded166e 100644
--- a/components/modal/doc/index.en-US.md
+++ b/components/modal/doc/index.en-US.md
@@ -42,6 +42,7 @@ The dialog is currently divided into 2 modes, `normal mode` and `confirm box mod
| nzKeyboard | Whether support press esc to close | `boolean` | `true` |
| nzMask | Whether show mask or not. | `boolean` | `true` | ✅ |
| nzMaskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | `boolean` | `true` | ✅ |
+| nzCloseOnNavigation | Whether to close the modal when the navigation history changes | `boolean` | `true` | ✅ |
| nzMaskStyle | Style for modal's mask element. | `object` | - |
| nzOkText | Text of the OK button. Set to null to show no ok button (this value is invalid if the nzFooter parameter is used in normal mode) | `string` | OK |
| nzOkType | Button type of the OK button. Consistent with the type of the `nz-button`. | `string` | primary |
diff --git a/components/modal/doc/index.zh-CN.md b/components/modal/doc/index.zh-CN.md
index 6dc9f9df570..0c1c49d4fb5 100644
--- a/components/modal/doc/index.zh-CN.md
+++ b/components/modal/doc/index.zh-CN.md
@@ -43,6 +43,7 @@ import { NzModalModule } from 'ng-zorro-antd/modal';
| nzKeyboard | 是否支持键盘esc关闭 | `boolean` | `true` |
| nzMask | 是否展示遮罩 | `boolean` | `true` | ✅ |
| nzMaskClosable | 点击蒙层是否允许关闭 | `boolean` | `true` | ✅ |
+| nzCloseOnNavigation | 导航历史变化时是否关闭模态框 | `boolean` | `true` | ✅ |
| nzMaskStyle | 遮罩样式 | `object` | - |
| nzOkText | 确认按钮文字。设为 null 表示不显示确认按钮(若在普通模式下使用了 nzFooter 参数,则该值无效) | `string` | 确定 |
| nzOkType | 确认按钮类型。与button的type类型值一致 | `string` | primary |
diff --git a/components/modal/modal-container.ts b/components/modal/modal-container.ts
index 5dbf93576c0..65ac8d31cd5 100644
--- a/components/modal/modal-container.ts
+++ b/components/modal/modal-container.ts
@@ -10,7 +10,7 @@ import { AnimationEvent } from '@angular/animations';
import { FocusTrap, FocusTrapFactory } from '@angular/cdk/a11y';
import { OverlayRef } from '@angular/cdk/overlay';
import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
-import { ChangeDetectorRef, ComponentRef, ElementRef, EmbeddedViewRef, EventEmitter, NgZone, Renderer2 } from '@angular/core';
+import { ChangeDetectorRef, ComponentRef, ElementRef, EmbeddedViewRef, EventEmitter, NgZone, OnDestroy, Renderer2 } from '@angular/core';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { getElementOffset } from 'ng-zorro-antd/core/util';
import { FADE_CLASS_NAME_MAP, MODAL_MASK_CLASS_NAME, ZOOM_CLASS_NAME_MAP } from './modal-config';
@@ -22,7 +22,7 @@ export function throwNzModalContentAlreadyAttachedError(): never {
throw Error('Attempting to attach modal content after content is already attached');
}
-export class BaseModalContainer extends BasePortalOutlet {
+export class BaseModalContainer extends BasePortalOutlet implements OnDestroy {
portalOutlet!: CdkPortalOutlet;
modalElementRef!: ElementRef;
@@ -30,6 +30,7 @@ export class BaseModalContainer extends BasePortalOutlet {
containerClick = new EventEmitter();
cancelTriggered = new EventEmitter();
okTriggered = new EventEmitter();
+ onDestroy = new EventEmitter();
state: 'void' | 'enter' | 'exit' = 'enter';
document: Document;
@@ -57,6 +58,10 @@ export class BaseModalContainer extends BasePortalOutlet {
this.setContainer();
}
+ ngOnDestroy(): void {
+ this.onDestroy.emit();
+ }
+
onMousedown(e: MouseEvent): void {
this.latestMousedownTarget = (e.target as HTMLElement) || null;
}
diff --git a/components/modal/modal-ref.ts b/components/modal/modal-ref.ts
index 98c5e5654a9..d5e0ab767f9 100644
--- a/components/modal/modal-ref.ts
+++ b/components/modal/modal-ref.ts
@@ -10,7 +10,7 @@ import { OverlayRef } from '@angular/cdk/overlay';
import { EventEmitter } from '@angular/core';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { isPromise } from 'ng-zorro-antd/core/util';
-import { Subject } from 'rxjs';
+import { merge, Subject } from 'rxjs';
import { filter, take } from 'rxjs/operators';
import { BaseModalContainer } from './modal-container';
@@ -51,14 +51,17 @@ export class NzModalRef implements NzModalLegacyAP
}
});
- containerInstance.animationStateChanged
- .pipe(
+ merge(
+ containerInstance.onDestroy,
+ containerInstance.animationStateChanged.pipe(
filter(event => event.phaseName === 'done' && event.toState === 'exit'),
take(1)
)
+ )
+ .pipe(take(1))
.subscribe(() => {
clearTimeout(this.closeTimeout);
- this.overlayRef.dispose();
+ this.finishDialogClose();
});
containerInstance.containerClick.pipe(take(1)).subscribe(() => {
@@ -138,10 +141,9 @@ export class NzModalRef implements NzModalLegacyAP
take(1)
)
.subscribe(event => {
- this.state = NzModalState.CLOSED;
this.overlayRef.detachBackdrop();
this.closeTimeout = setTimeout(() => {
- this.overlayRef.dispose();
+ this.finishDialogClose();
}, event.totalTime + 100);
});
@@ -196,4 +198,9 @@ export class NzModalRef implements NzModalLegacyAP
this.close(result);
}
}
+
+ private finishDialogClose(): void {
+ this.state = NzModalState.CLOSED;
+ this.overlayRef.dispose();
+ }
}
diff --git a/components/modal/modal.component.ts b/components/modal/modal.component.ts
index 881a0db4b5f..1a8ebc5d904 100644
--- a/components/modal/modal.component.ts
+++ b/components/modal/modal.component.ts
@@ -47,6 +47,7 @@ const NZ_CONFIG_COMPONENT_NAME = 'modal';
export class NzModalComponent implements OnChanges, NzModalLegacyAPI {
static ngAcceptInputType_nzMask: BooleanInput;
static ngAcceptInputType_nzMaskClosable: BooleanInput;
+ static ngAcceptInputType_nzCloseOnNavigation: BooleanInput;
static ngAcceptInputType_nzVisible: BooleanInput;
static ngAcceptInputType_nzClosable: BooleanInput;
static ngAcceptInputType_nzOkLoading: BooleanInput;
@@ -58,6 +59,7 @@ export class NzModalComponent implements OnChanges
@Input() @WithConfig(NZ_CONFIG_COMPONENT_NAME) @InputBoolean() nzMask: boolean = true;
@Input() @WithConfig(NZ_CONFIG_COMPONENT_NAME) @InputBoolean() nzMaskClosable: boolean = true;
+ @Input() @WithConfig(NZ_CONFIG_COMPONENT_NAME) @InputBoolean() nzCloseOnNavigation: boolean = true;
@Input() @InputBoolean() nzVisible: boolean = false;
@Input() @InputBoolean() nzClosable: boolean = true;
@Input() @InputBoolean() nzOkLoading: boolean = false;
diff --git a/components/modal/modal.spec.ts b/components/modal/modal.spec.ts
index 22f308ab969..8177742af2a 100644
--- a/components/modal/modal.spec.ts
+++ b/components/modal/modal.spec.ts
@@ -1384,6 +1384,20 @@ describe('NzModal', () => {
modalInstance.triggerCancel();
}).not.toThrowError();
}));
+
+ it('should close when the host view is destroyed', fakeAsync(() => {
+ componentInstance.isVisible = true;
+ componentFixture.detectChanges();
+ flush();
+
+ expect(overlayContainerElement.querySelector('nz-modal-container')).not.toBeNull();
+
+ componentFixture.destroy();
+ componentFixture.detectChanges();
+ flush();
+
+ expect(overlayContainerElement.querySelector('nz-modal-container')).toBeNull();
+ }));
});
});
diff --git a/components/modal/utils.ts b/components/modal/utils.ts
index cff5ec55b5a..7550650343e 100644
--- a/components/modal/utils.ts
+++ b/components/modal/utils.ts
@@ -54,7 +54,8 @@ export function getConfigFromComponent(component: NzModalComponent): ModalOption
nzOnOk,
nzOnCancel,
nzAfterOpen,
- nzAfterClose
+ nzAfterClose,
+ nzCloseOnNavigation
} = component;
return {
nzMask,
@@ -87,6 +88,7 @@ export function getConfigFromComponent(component: NzModalComponent): ModalOption
nzOnOk,
nzOnCancel,
nzAfterOpen,
- nzAfterClose
+ nzAfterClose,
+ nzCloseOnNavigation
};
}