From f5410cbba8af865019826274efb8daf084645a4f Mon Sep 17 00:00:00 2001 From: Wilson Zeng Date: Sat, 17 Mar 2018 21:36:46 +0800 Subject: [PATCH] feat(module:modal): add afterClose and more to improve communication capabilities close #1155 --- components/modal/demo/async.ts | 6 +- components/modal/demo/basic.ts | 8 +-- components/modal/demo/confirm-promise.ts | 4 +- components/modal/demo/footer.ts | 10 +-- components/modal/demo/service.ts | 15 ++-- components/modal/doc/index.en-US.md | 21 +++--- components/modal/doc/index.zh-CN.md | 25 +++---- components/modal/modal-public-agent.class.ts | 29 -------- components/modal/nz-modal-ref.class.ts | 37 ++++++++++ components/modal/nz-modal.component.html | 8 +-- components/modal/nz-modal.component.ts | 73 ++++++++++---------- components/modal/nz-modal.service.ts | 18 ++--- components/modal/nz-modal.spec.ts | 30 ++++---- components/modal/nz-modal.type.ts | 31 +++++---- components/modal/public-api.ts | 2 +- 15 files changed, 167 insertions(+), 150 deletions(-) delete mode 100644 components/modal/modal-public-agent.class.ts create mode 100644 components/modal/nz-modal-ref.class.ts diff --git a/components/modal/demo/async.ts b/components/modal/demo/async.ts index 5e95a478eeb..13638d3a2ee 100644 --- a/components/modal/demo/async.ts +++ b/components/modal/demo/async.ts @@ -6,7 +6,7 @@ import { Component } from '@angular/core'; - +

Modal Content

`, @@ -20,7 +20,7 @@ export class NzDemoModalAsyncComponent { this.isVisible = true; } - handleOk($event: MouseEvent): void { + handleOk(): void { this.isOkLoading = true; window.setTimeout(() => { this.isVisible = false; @@ -28,7 +28,7 @@ export class NzDemoModalAsyncComponent { }, 3000); } - handleCancel($event: MouseEvent): void { + handleCancel(): void { this.isVisible = false; } } diff --git a/components/modal/demo/basic.ts b/components/modal/demo/basic.ts index bd6de93f062..e0a1be5a128 100644 --- a/components/modal/demo/basic.ts +++ b/components/modal/demo/basic.ts @@ -4,7 +4,7 @@ import { Component } from '@angular/core'; selector: 'nz-demo-modal-basic', template: ` - +

Content one

Content two

Content three

@@ -21,13 +21,13 @@ export class NzDemoModalBasicComponent { this.isVisible = true; } - handleOk($event: MouseEvent): void { + handleOk(): void { console.log('Button ok clicked!'); this.isVisible = false; } - handleCancel($event: MouseEvent): void { - console.log('Button cancel clicked!', $event); + handleCancel(): void { + console.log('Button cancel clicked!'); this.isVisible = false; } } diff --git a/components/modal/demo/confirm-promise.ts b/components/modal/demo/confirm-promise.ts index 6d8b0299a3b..cee05281fa2 100644 --- a/components/modal/demo/confirm-promise.ts +++ b/components/modal/demo/confirm-promise.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { ModalPublicAgent, NzModalService } from 'ng-zorro-antd'; +import { NzModalRef, NzModalService } from 'ng-zorro-antd'; @Component({ selector: 'nz-demo-modal-confirm-promise', @@ -9,7 +9,7 @@ import { ModalPublicAgent, NzModalService } from 'ng-zorro-antd'; styles : [] }) export class NzDemoModalConfirmPromiseComponent { - confirmModal: ModalPublicAgent; // For testing by now + confirmModal: NzModalRef; // For testing by now constructor(private modal: NzModalService) { } diff --git a/components/modal/demo/footer.ts b/components/modal/demo/footer.ts index 83fd83b3f09..33a4f981418 100644 --- a/components/modal/demo/footer.ts +++ b/components/modal/demo/footer.ts @@ -7,7 +7,7 @@ import { Component } from '@angular/core'; - + Custom Modal Title @@ -22,8 +22,8 @@ import { Component } from '@angular/core'; Modal Footer: - - + + `, @@ -39,7 +39,7 @@ export class NzDemoModalFooterComponent { this.isVisible = true; } - handleOk($event: MouseEvent): void { + handleOk(): void { this.isConfirmLoading = true; setTimeout(() => { this.isVisible = false; @@ -47,7 +47,7 @@ export class NzDemoModalFooterComponent { }, 3000); } - handleCancel($event: MouseEvent): void { + handleCancel(): void { this.isVisible = false; } } diff --git a/components/modal/demo/service.ts b/components/modal/demo/service.ts index af58d65ec34..e1997328464 100644 --- a/components/modal/demo/service.ts +++ b/components/modal/demo/service.ts @@ -1,7 +1,7 @@ /* entryComponents: NzModalCustomComponent */ import { Component, Input, TemplateRef } from '@angular/core'; -import { ModalPublicAgent, NzModalService } from 'ng-zorro-antd'; +import { NzModalRef, NzModalService } from 'ng-zorro-antd'; @Component({ selector: 'nz-demo-modal-service', @@ -37,7 +37,7 @@ import { ModalPublicAgent, NzModalService } from 'ng-zorro-antd'; ` }) export class NzDemoModalServiceComponent { - tplModal: ModalPublicAgent; + tplModal: NzModalRef; tplModalButtonLoading = false; constructor(private modalService: NzModalService) { } @@ -80,15 +80,18 @@ export class NzDemoModalServiceComponent { }, nzFooter: [{ label: 'change component tilte from outside', - onClick: (componentInstance: NzModalCustomComponent) => { + onClick: (componentInstance) => { componentInstance.title = 'title in inner component is changed'; } }] }); + // Return a result when closed + modal.afterClose().subscribe((result) => console.log('[afterClose] The result is:', result)); + // delay until modal instance created window.setTimeout(() => { - const instance = modal.getContentComponentRef().instance as NzModalCustomComponent; + const instance = modal.getContentComponent(); instance.subtitle = 'sub title is changed'; }, 2000); } @@ -149,9 +152,9 @@ export class NzModalCustomComponent { @Input() title: string; @Input() subtitle: string; - constructor(private modal: ModalPublicAgent) { } + constructor(private modal: NzModalRef) { } destroyModal(): void { - this.modal.destroy(); + this.modal.destroy({ data: 'this the result data' }); } } diff --git a/components/modal/doc/index.en-US.md b/components/modal/doc/index.en-US.md index c228e4f98b7..a5a3316c7fa 100644 --- a/components/modal/doc/index.en-US.md +++ b/components/modal/doc/index.en-US.md @@ -14,7 +14,7 @@ getting feedback or information purposes. Additionally, if you need show a simple confirmation dialog, you can use `NzModalService.confirm()`, and so on. -It is recommended to use the `Component` way to pop up the Modal, so that the component logic of the popup layer can be completely isolated from the outer component, and can be reused at any time. In the popup layer component, you can obtain Modal's component instance by injecting `ModalPublicAgent` to control the behavior of the modal box. +It is recommended to use the `Component` way to pop up the Modal, so that the component logic of the popup layer can be completely isolated from the outer component, and can be reused at any time. In the popup layer component, you can obtain Modal's component instance by injecting `NzModalRef` to control the behavior of the modal box. ## API @@ -30,7 +30,7 @@ The dialog is currently divided into 2 modes, `normal mode` and `confirm box mod | nzOkLoading | Whether to apply loading visual effect for OK button or not | boolean | false | | nzCancelLoading | Whether to apply loading visual effect for Cancel button or not | boolean | false | | nzFooter | Footer content, set as footer=null when you don't need default buttons. 1. Only valid in normal mode.
2. You can customize the buttons to the maximum extent by passing a `ModalButtonOptions` configuration (see the case or the instructions below).
| string
TemplateRef
ModalButtonOptions | OK and Cancel buttons | -| nzGetContainer | The mount node for Modal | HTMLElement / () => HTMLElement| Handled by overlay container | +| nzGetContainer | The mount node for Modal | HTMLElement / () => HTMLElement| A default container | | 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 | | nzMaskStyle | Style for modal's mask element. | object | - | @@ -42,8 +42,8 @@ The dialog is currently divided into 2 modes, `normal mode` and `confirm box mod | nzWidth | Width of the modal dialog. When using numbers, the default unit is `px` | string
number | 520 | | nzWrapClassName | The class name of the container of the modal dialog | string | - | | nzZIndex | The z-index of the Modal | number | 1000 | -| nzOnCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button. Note: When created with `NzModalService.create`, this parameter should be passed into the type of function (callback function). This function returns a promise, which is automatically closed when the execution is complete or the promise ends (return `false` to prevent closing) | EventEmitter | - | -| nzOnOk | Specify a EventEmitter that will be emitted when a user clicks the OK button | EventEmitter | 无 | +| nzOnCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button (If nzContent is Component, the Component instance will be put in as an argument). Note: When created with `NzModalService.create`, this parameter should be passed into the type of function (callback function). This function returns a promise, which is automatically closed when the execution is complete or the promise ends (return `false` to prevent closing) | EventEmitter | - | +| nzOnOk | Specify a EventEmitter that will be emitted when a user clicks the OK button (If nzContent is Component, the Component instance will be put in as an argument). Note: When created with `NzModalService.create`, this parameter should be passed into the type of function (callback function). This function returns a promise, which is automatically closed when the execution is complete or the promise ends (return `false` to prevent closing) | EventEmitter | 无 | | nzContent | Content | string / TemplateRef / Component / ng-content | - | | nzComponentParams | When nzContent is a Component, the attributes in this parameter will be passed to the nzContent instance | object | - | | nzIconType | Icon type of the Icon component. Only valid in confirm box mode | string | question-circle | @@ -71,8 +71,8 @@ Consistent with the above API, some property types or initial values are differe | Property | Description | Type | Default | |------------|----------------|------------------|---------------| -| nzOnOk | Specify a EventEmitter that will be emitted when a user clicks the OK button. This function returns a promise, which is automatically closed when the execution is complete or the promise ends (return `false` to prevent closing) | function | - | -| nzOnCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button. This function returns a promise, which is automatically closed when the execution is complete or the promise ends (return `false` to prevent closing) | function | - | +| nzOnOk | Specify a EventEmitter that will be emitted when a user clicks the OK button (If nzContent is Component, the Component instance will be put in as an argument.). This function returns a promise, which is automatically closed when the execution is complete or the promise ends (return `false` to prevent closing) | function | - | +| nzOnCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button (If nzContent is Component, the Component instance will be put in as an argument.). This function returns a promise, which is automatically closed when the execution is complete or the promise ends (return `false` to prevent closing) | function | - | | nzWidth | Width of the modal dialog | string / number | 416 | | nzMaskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | false | @@ -80,23 +80,24 @@ All the `NzModalService.method`s will return a reference, and then we can close ```ts constructor(modal: NzModalService) { - const ref: ModalPublicAgent = modal.info(); + const ref: NzModalRef = modal.info(); ref.destroy(); // Note: This dialog will be destroyed directly } ``` ### Related type definition -#### ModalPublicAgent (used for control dialogs) +#### NzModalRef (used for control dialogs) -The dialog created by the service method `NzModalService.xxx()` will return a `ModalPublicAgent` object that is used to manipulate the dialog (this object can also be obtained by dependency injection `ModalPublicAgent` if `nzContent` is used as Component) , This object has the following methods: +The dialog created by the service method `NzModalService.xxx()` will return a `NzModalRef` object that is used to manipulate the dialog (this object can also be obtained by dependency injection `NzModalRef` if `nzContent` is used as Component) , This object has the following methods: | Method | Description | |----|----| | open() | Open (display) dialog box. Calling this function will fail if the dialog is already destroyed | | close() | Close (hide) the dialog. Note: When used for a dialog created as a service, this method will destroy the dialog directly (as with the destroy method) | | destroy() | Destroy the dialog. Note: Used only for dialogs created by the service (non-service created dialogs, this method only hides the dialog) | -| getContentComponentRef() | Gets a Component reference (of type `ComponentRef`) in the contents of the dialog for `nzContent`. Note: When the dialog is not initialized (`ngOnInit` is not executed), this function will return `undefined` | +| afterClose() | Returns an Observable object to get the result parameter passed in close/destroy (will fire after the dialog is closed) | +| getContentComponent() | Gets the Component instance in the contents of the dialog for `nzContent`. Note: When the dialog is not initialized (`ngOnInit` is not executed), this function will return `undefined` | #### ModalButtonOptions (used to customize the bottom button) diff --git a/components/modal/doc/index.zh-CN.md b/components/modal/doc/index.zh-CN.md index d47fbd485ff..8eaf0b65694 100644 --- a/components/modal/doc/index.zh-CN.md +++ b/components/modal/doc/index.zh-CN.md @@ -15,7 +15,7 @@ title: Modal 推荐使用加载Component的方式弹出Modal,这样弹出层的Component逻辑可以与外层Component完全隔离,并且做到可以随时复用, -在弹出层Component中可以通过依赖注入`ModalPublicAgent`方式直接获取模态框的组件实例,用于控制在弹出层组件中控制模态框行为。 +在弹出层Component中可以通过依赖注入`NzModalRef`方式直接获取模态框的组件实例,用于控制在弹出层组件中控制模态框行为。 ## API @@ -30,7 +30,7 @@ title: Modal | nzOkLoading | 确定按钮 loading | boolean | false | | nzCancelLoading | 取消按钮 loading | boolean | false | | nzFooter | 底部内容。1. 仅在普通模式下有效。
2. 可通过传入 ModalButtonOptions 来最大程度自定义按钮(详见案例或下方说明)。
3. 当不需要底部时,可以设为 null
| string
TemplateRef
ModalButtonOptions | 默认的确定取消按钮 | -| nzGetContainer | 指定 Modal 挂载的 HTML 节点 | HTMLElement
() => HTMLElement| 默认在overlay容器中 | +| nzGetContainer | 指定 Modal 挂载的 HTML 节点 | HTMLElement
() => HTMLElement| 默认容器 | | nzMask | 是否展示遮罩 | boolean | true | | nzMaskClosable | 点击蒙层是否允许关闭 | boolean | true | | nzMaskStyle | 遮罩样式 | object | 无 | @@ -42,8 +42,8 @@ title: Modal | nzWidth | 宽度。使用数字时,默认单位为px | string
number | 520 | | nzWrapClassName | 对话框外层容器的类名 | string | 无 | | nzZIndex | 设置 Modal 的 `z-index` | number | 1000 | -| nzOnCancel | 点击遮罩层或右上角叉或取消按钮的回调。注:当以`NzModalService.create`创建时,此参数应传入function(回调函数)。该函数可返回promise,待执行完毕或promise结束时,将自动关闭对话框(返回false可阻止关闭) | EventEmitter | 无 | -| nzOnOk | 点击确定回调 | EventEmitter | 无 | +| nzOnCancel | 点击遮罩层或右上角叉或取消按钮的回调(若nzContent为Component,则将会以该Component实例作为参数)。注:当以`NzModalService.create`创建时,此参数应传入function(回调函数)。该函数可返回promise,待执行完毕或promise结束时,将自动关闭对话框(返回false可阻止关闭) | EventEmitter | 无 | +| nzOnOk | 点击确定回调(若nzContent为Component,则将会以该Component实例作为参数)。注:当以`NzModalService.create`创建时,此参数应传入function(回调函数)。该函数可返回promise,待执行完毕或promise结束时,将自动关闭对话框(返回false可阻止关闭) | EventEmitter | 无 | | nzContent | 内容 | string
TemplateRef
Component
ng-content | 无 | | nzComponentParams | 当nzContent为组件类(Component)时,该参数中的属性将传入nzContent实例中 | object | 无 | | nzIconType | 图标 Icon 类型。仅 确认框模式 下有效 | string | question-circle | @@ -70,8 +70,8 @@ title: Modal | 参数 | 说明 | 类型 | 默认值 | |------------|----------------|------------------|--------------| -| nzOnOk | 点击确定按钮时将执行的回调函数。该函数可返回promise,待执行完毕或promise结束时,将自动关闭对话框(返回false可阻止关闭) | function | 无 | -| nzOnCancel | 点击遮罩层或右上角叉或取消按钮的回调。该函数可返回promise,待执行完毕或promise结束时,将自动关闭对话框(返回false可阻止关闭) | function | 无 | +| nzOnOk | 点击确定按钮时将执行的回调函数(若nzContent为Component,则将会以该Component实例作为参数)。该函数可返回promise,待执行完毕或promise结束时,将自动关闭对话框(返回false可阻止关闭) | function | 无 | +| nzOnCancel | 点击遮罩层或右上角叉或取消按钮的回调(若nzContent为Component,则将会以该Component实例作为参数)。该函数可返回promise,待执行完毕或promise结束时,将自动关闭对话框(返回false可阻止关闭) | function | 无 | | nzWidth | 宽度 | string
number | 416 | | nzMaskClosable | 点击蒙层是否允许关闭 | boolean | false | @@ -79,23 +79,24 @@ title: Modal ```ts constructor(modal: NzModalService) { - const ref: ModalPublicAgent = modal.info(); + const ref: NzModalRef = modal.info(); ref.destroy(); // 注:这里将直接销毁对话框 } ``` ### 相关类型定义 -#### ModalPublicAgent(用于控制对话框) +#### NzModalRef(用于控制对话框) -通过服务方式 `NzModalService.xxx()` 创建的对话框,都会返回一个 `ModalPublicAgent` 对象,用于操控该对话框(若使用nzContent为Component时,也可通过依赖注入 `ModalPublicAgent` 方式获得此对象),该对象具有以下方法: +通过服务方式 `NzModalService.xxx()` 创建的对话框,都会返回一个 `NzModalRef` 对象,用于操控该对话框(若使用nzContent为Component时,也可通过依赖注入 `NzModalRef` 方式获得此对象),该对象具有以下方法: | 方法 | 说明 | |----|----| | open() | 打开(显示)对话框。若对话框已销毁,则调用此函数将失效 | -| close() | 关闭(隐藏)对话框。注:当用于以服务方式创建的对话框,此方法将直接 销毁 对话框(同destroy方法) | -| destroy() | 销毁对话框。注:仅用于服务方式创建的对话框(非服务方式创建的对话框,此方法只会隐藏对话框) | -| getContentComponentRef() | 获取对话框内容中`nzContent`的Component引用(类型为`ComponentRef`)。注:当对话框还未初始化完毕(`ngOnInit`未执行)时,此函数将返回`undefined` | +| close(result: any) | 关闭(隐藏)对话框。注:当用于以服务方式创建的对话框,此方法将直接 销毁 对话框(同destroy方法) | +| destroy(result: any) | 销毁对话框。注:仅用于服务方式创建的对话框(非服务方式创建的对话框,此方法只会隐藏对话框) | +| afterClose() | 返回一个Observable对象来获取close/destroy中传递的result参数(将在对话框关闭后触发) | +| getContentComponent() | 获取对话框内容中`nzContent`的Component实例instance。注:当对话框还未初始化完毕(`ngOnInit`未执行)时,此函数将返回`undefined` | #### ModalButtonOptions(用于自定义底部按钮) diff --git a/components/modal/modal-public-agent.class.ts b/components/modal/modal-public-agent.class.ts deleted file mode 100644 index 6c6ec112fd2..00000000000 --- a/components/modal/modal-public-agent.class.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ComponentRef } from '@angular/core'; - -import { NzModalComponent } from './nz-modal.component'; - -/** - * API class that public to users to handle the modal instance. - * ModalPublicAgent is aim to avoid accessing to the modal instance directly by users. - */ -export abstract class ModalPublicAgent { - abstract open(): void; - abstract close(): void; - abstract destroy(): void; - - /** - * Return the ComponentRef of nzContent when specify nzContent as a Component - * Note: this method may return undefined if the Component has not ready yet. (it only available after Modal's ngOnInit) - */ - abstract getContentComponentRef(): ComponentRef<{}>; - - /** - * Get the dom element of this Modal - */ - abstract getElement(): HTMLElement; - - /** - * Get the instance of the Modal itself - */ - abstract getInstance(): NzModalComponent; -} diff --git a/components/modal/nz-modal-ref.class.ts b/components/modal/nz-modal-ref.class.ts new file mode 100644 index 00000000000..632c124efed --- /dev/null +++ b/components/modal/nz-modal-ref.class.ts @@ -0,0 +1,37 @@ +import { ComponentRef, Type } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; + +import { NzModalComponent } from './nz-modal.component'; + +/** + * API class that public to users to handle the modal instance. + * NzModalRef is aim to avoid accessing to the modal instance directly by users. + */ +export abstract class NzModalRef { // tslint:disable-line:no-any + abstract open(): void; + abstract close(result?: R): void; + abstract destroy(result?: R): void; + abstract afterClose(): Observable; + + // /** + // * Return the ComponentRef of nzContent when specify nzContent as a Component + // * Note: this method may return undefined if the Component has not ready yet. (it only available after Modal's ngOnInit) + // */ + // abstract getContentComponentRef(): ComponentRef<{}>; + + /** + * Return the component instance of nzContent when specify nzContent as a Component + * Note: this method may return undefined if the Component has not ready yet. (it only available after Modal's ngOnInit) + */ + abstract getContentComponent(): T; + + /** + * Get the dom element of this Modal + */ + abstract getElement(): HTMLElement; + + /** + * Get the instance of the Modal itself + */ + abstract getInstance(): NzModalComponent; +} diff --git a/components/modal/nz-modal.component.html b/components/modal/nz-modal.component.html index da04a591470..2879da0a490 100644 --- a/components/modal/nz-modal.component.html +++ b/components/modal/nz-modal.component.html @@ -74,11 +74,11 @@ >{{ button.label }} - - @@ -111,11 +111,11 @@
- - diff --git a/components/modal/nz-modal.component.ts b/components/modal/nz-modal.component.ts index 20965811575..5a8f4eba4de 100644 --- a/components/modal/nz-modal.component.ts +++ b/components/modal/nz-modal.component.ts @@ -20,12 +20,13 @@ import { ViewChild, ViewContainerRef } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; import { measureScrollbar } from '../core/util/mesure-scrollbar'; import { NzI18nService } from '../i18n/nz-i18n.service'; -import { ModalPublicAgent } from './modal-public-agent.class'; import ModalUtil from './modal-util'; +import { NzModalRef } from './nz-modal-ref.class'; import { ModalButtonOptions, ModalOptions, ModalType, OnClickCallback } from './nz-modal.type'; export const MODAL_ANIMATE_DURATION = 200; // Duration when perform animations (ms) @@ -41,12 +42,13 @@ type AnimationState = 'enter' | 'leave' | null; templateUrl: './nz-modal.component.html' }) -export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChanges, AfterViewInit, ModalOptions { +// tslint:disable-next-line:no-any +export class NzModalComponent extends NzModalRef implements OnInit, OnChanges, AfterViewInit, ModalOptions { @Input() nzModalType: ModalType = 'default'; - @Input() nzContent: string | TemplateRef<{}> | Type<{}>; // [STATIC] If not specified, will use + @Input() nzContent: string | TemplateRef<{}> | Type; // [STATIC] If not specified, will use @Input() nzComponentParams: object; // [STATIC] ONLY avaliable when nzContent is a component - @Input() nzFooter: string | TemplateRef<{}> | ModalButtonOptions[]; // [STATIC] Default Modal ONLY - @Input() nzGetContainer: HTMLElement | (() => HTMLElement); // [STATIC] + @Input() nzFooter: string | TemplateRef<{}> | Array>; // [STATIC] Default Modal ONLY + @Input() nzGetContainer: HTMLElement | (() => HTMLElement) = () => this.overlay.create().overlayElement; // [STATIC] @Input() nzVisible = false; @Output() nzVisibleChange = new EventEmitter(); @@ -62,17 +64,17 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan @Input() nzMaskClosable = true; @Input() nzMaskStyle: object; @Input() nzBodyStyle: object; - @Output() nzAfterClose = new EventEmitter(); // Trigger when modal is hidden + @Output() nzAfterClose = new EventEmitter(); // Trigger when modal is hidden // --- Predefined OK & Cancel buttons @Input() nzOkText: string; @Input() nzOkType = 'primary'; @Input() nzOkLoading = false; - @Input() @Output() nzOnOk: EventEmitter | OnClickCallback = new EventEmitter(); + @Input() @Output() nzOnOk: EventEmitter | OnClickCallback = new EventEmitter(); @ViewChild('autoFocusButtonOk', { read: ElementRef }) autoFocusButtonOk: ElementRef; // Only aim to focus the ok button that needs to be auto focused @Input() nzCancelText: string; @Input() nzCancelLoading = false; - @Input() @Output() nzOnCancel: EventEmitter | OnClickCallback = new EventEmitter(); + @Input() @Output() nzOnCancel: EventEmitter | OnClickCallback = new EventEmitter(); @ViewChild('modalContainer') modalContainer: ElementRef; @ViewChild('bodyContainer', { read: ViewContainerRef }) bodyContainer: ViewContainerRef; @@ -84,7 +86,7 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan modalAnimationClassMap: object; transformOrigin = '0px 0px 0px'; // The origin point that animation based on - private contentComponentRef: ComponentRef<{}>; // Handle the reference when using nzContent as Component + private contentComponentRef: ComponentRef; // Handle the reference when using nzContent as Component private animationState: AnimationState; // Current animation state constructor(private overlay: Overlay, @@ -100,18 +102,16 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan ngOnInit(): void { if (this.isComponent(this.nzContent)) { - this.createDynamicComponent(this.nzContent as Type<{}>); // Create component along without View + this.createDynamicComponent(this.nzContent as Type); // Create component along without View } if (this.isModalButtons(this.nzFooter)) { // Setup default button options - this.nzFooter = this.formatModalButtons(this.nzFooter as ModalButtonOptions[]); + this.nzFooter = this.formatModalButtons(this.nzFooter as Array>); } const container = typeof this.nzGetContainer === 'function' ? this.nzGetContainer() : this.nzGetContainer; if (container instanceof HTMLElement) { container.appendChild(this.elementRef.nativeElement); - } else { // Use overlay to handle this modal by default - this.overlay.create().overlayElement.appendChild(this.elementRef.nativeElement); } } @@ -143,30 +143,37 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan this.changeVisibleFromInside(true); } - close(): void { - this.changeVisibleFromInside(false).then(() => this.nzAfterClose.emit()); + close(result?: R): void { + this.changeVisibleFromInside(false).then(() => this.nzAfterClose.emit(result)); } - destroy(): void { // Destroy equals Close - this.close(); + destroy(result?: R): void { // Destroy equals Close + this.close(result); + } + + afterClose(): Observable { + return this.nzAfterClose.asObservable(); } getInstance(): NzModalComponent { return this; } - getContentComponentRef(): ComponentRef<{}> { + getContentComponentRef(): ComponentRef { return this.contentComponentRef; } + getContentComponent(): T { + return this.contentComponentRef && this.contentComponentRef.instance; + } + getElement(): HTMLElement { return this.elementRef && this.elementRef.nativeElement; } onClickMask($event: MouseEvent): void { if (this.nzMask && this.nzMaskClosable && ($event.target as HTMLElement).classList.contains('ant-modal-wrap')) { - // this.close(); - this.onClickOkCancel($event, 'cancel'); + this.onClickOkCancel('cancel'); } } @@ -174,18 +181,17 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan return this.nzModalType === type; } - private onClickCloseBtn($event: MouseEvent): void { - // this.close(); - this.onClickOkCancel($event, 'cancel'); + private onClickCloseBtn(): void { + this.onClickOkCancel('cancel'); } - private onClickOkCancel($event: MouseEvent, type: 'ok' | 'cancel'): void { + private onClickOkCancel(type: 'ok' | 'cancel'): void { const trigger = { 'ok': this.nzOnOk, 'cancel': this.nzOnCancel }[ type ]; const loadingKey = { 'ok': 'nzOkLoading', 'cancel': 'nzCancelLoading' }[ type ]; if (trigger instanceof EventEmitter) { - trigger.emit($event); + trigger.emit(this.getContentComponent()); } else if (typeof trigger === 'function') { - const result = trigger($event); + const result = trigger(this.getContentComponent()); const caseClose = (doClose: boolean | void | {}) => (doClose !== false) && this.close(); // Users can return "false" to prevent closing by default if (isPromise(result)) { this[ loadingKey ] = true; @@ -217,7 +223,7 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan } // Lookup a button's property, if the prop is a function, call & then return the result, otherwise, return itself. - private getButtonCallableProp(options: ModalButtonOptions, prop: string): {} { + private getButtonCallableProp(options: ModalButtonOptions, prop: string): {} { const value = options[ prop ]; const args = []; if (this.contentComponentRef) { @@ -227,7 +233,7 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan } // On nzFooter's modal button click - private onButtonClick(button: ModalButtonOptions): void { + private onButtonClick(button: ModalButtonOptions): void { const result = this.getButtonCallableProp(button, 'onClick'); // Call onClick directly if (isPromise(result)) { button.loading = true; @@ -275,7 +281,7 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan }, MODAL_ANIMATE_DURATION)); } - private formatModalButtons(buttons: ModalButtonOptions[]): ModalButtonOptions[] { + private formatModalButtons(buttons: Array>): Array> { return buttons.map((button) => { const mixedButton = { ...{ @@ -299,14 +305,11 @@ export class NzModalComponent extends ModalPublicAgent implements OnInit, OnChan * Create a component dynamically but not attach to any View (this action will be executed when bodyContainer is ready) * @param component Component class */ - private createDynamicComponent(component: Type<{}>): void { + private createDynamicComponent(component: Type): void { const factory = this.cfr.resolveComponentFactory(component); const childInjector = Injector.create({ - providers: [ { - provide : ModalPublicAgent, - useValue: this - } ], - parent : this.viewContainer.parentInjector + providers: [ { provide : NzModalRef, useValue: this } ], + parent: this.viewContainer.parentInjector }); this.contentComponentRef = factory.create(childInjector); if (this.nzComponentParams) { diff --git a/components/modal/nz-modal.service.ts b/components/modal/nz-modal.service.ts index b6ef8069af7..7a57d56797a 100644 --- a/components/modal/nz-modal.service.ts +++ b/components/modal/nz-modal.service.ts @@ -4,7 +4,7 @@ import { ApplicationRef, ComponentFactoryResolver, ComponentRef, EventEmitter, I import { LoggerService } from '../core/util/logger/logger.service'; -import { ModalPublicAgent } from './modal-public-agent.class'; +import { NzModalRef } from './nz-modal-ref.class'; import { NzModalComponent } from './nz-modal.component'; import { ConfirmType, ModalOptions, ModalOptionsForService } from './nz-modal.type'; @@ -17,7 +17,7 @@ export class ModalBuilderForService { this.createModal(); if (!('nzGetContainer' in options)) { // As we use CDK to create modal in service by force, there is no need to use nzGetContainer - options.nzGetContainer = null; + options.nzGetContainer = null; // Override nzGetContainer's default value to prevent creating another overlay } this.changeProps(options); @@ -54,7 +54,7 @@ export class NzModalService { constructor(private overlay: Overlay, private logger: LoggerService) { } - create(options: ModalOptionsForService = {}): ModalPublicAgent { + create(options: ModalOptionsForService = {}): NzModalRef { if (typeof options.nzOnCancel !== 'function') { options.nzOnCancel = () => {}; // Leave a empty function to close this modal by default } @@ -62,7 +62,7 @@ export class NzModalService { return new ModalBuilderForService(this.overlay, options).getInstance(); } - confirm(options: ModalOptionsForService = {}, confirmType: ConfirmType = 'confirm'): ModalPublicAgent { + confirm(options: ModalOptionsForService = {}, confirmType: ConfirmType = 'confirm'): NzModalRef { if ('nzFooter' in options) { this.logger.warn(`The Confirm-Modal doesn't support "nzFooter", this property will be ignored.`); } @@ -79,23 +79,23 @@ export class NzModalService { return this.create(options); } - info(options: ModalOptionsForService = {}): ModalPublicAgent { + info(options: ModalOptionsForService = {}): NzModalRef { return this.simpleConfirm(options, 'info'); } - success(options: ModalOptionsForService = {}): ModalPublicAgent { + success(options: ModalOptionsForService = {}): NzModalRef { return this.simpleConfirm(options, 'success'); } - error(options: ModalOptionsForService = {}): ModalPublicAgent { + error(options: ModalOptionsForService = {}): NzModalRef { return this.simpleConfirm(options, 'error'); } - warning(options: ModalOptionsForService = {}): ModalPublicAgent { + warning(options: ModalOptionsForService = {}): NzModalRef { return this.simpleConfirm(options, 'warning'); } - private simpleConfirm(options: ModalOptionsForService = {}, confirmType: ConfirmType): ModalPublicAgent { + private simpleConfirm(options: ModalOptionsForService = {}, confirmType: ConfirmType): NzModalRef { if (!('nzIconType' in options)) { options.nzIconType = { 'info': 'info-circle', 'success': 'check-circle', 'error': 'cross-circle', 'warning': 'exclamation-circle' }[ confirmType ]; } diff --git a/components/modal/nz-modal.spec.ts b/components/modal/nz-modal.spec.ts index 45f0bda48e2..9b7c5794cd9 100644 --- a/components/modal/nz-modal.spec.ts +++ b/components/modal/nz-modal.spec.ts @@ -7,7 +7,7 @@ import { NzButtonComponent } from '../button/nz-button.component'; import { NzButtonModule } from '../button/nz-button.module'; import { CssUnitPipe } from './css-unit.pipe'; -import { ModalPublicAgent } from './modal-public-agent.class'; +import { NzModalRef } from './nz-modal-ref.class'; import { MODAL_ANIMATE_DURATION, NzModalComponent } from './nz-modal.component'; import { NzModalModule } from './nz-modal.module'; import { NzModalService } from './nz-modal.service'; @@ -57,7 +57,7 @@ describe('modal', () => { describe('demo-confirm-promise', () => { const tempModalId = generateUniqueId(); // Temp unique id to mark the confirm modal that created by service - let modalAgent: ModalPublicAgent; + let modalAgent: NzModalRef; let buttonShow: HTMLButtonElement; beforeEach(async(() => { @@ -103,7 +103,7 @@ describe('modal', () => { describe('NormalModal: created by service with most APIs', () => { const tempModalId = generateUniqueId(); // Temp unique id to mark the confirm modal that created by service - let modalAgent: ModalPublicAgent; + let modalAgent: NzModalRef; let modalElement: HTMLElement; beforeEach(async(() => { @@ -169,7 +169,7 @@ describe('modal', () => { describe('NormalModal: created by service with vary nzContent and nzFooter', () => { const tempModalId = generateUniqueId(); // Temp unique id to mark the confirm modal that created by service - let modalAgent: ModalPublicAgent; + let modalAgent: NzModalRef; let modalElement: HTMLElement; beforeEach(async(() => { @@ -193,7 +193,7 @@ describe('modal', () => { it('should change title from in/outside and trigger button', fakeAsync(() => { fixture.detectChanges(); // Initial change detecting - const contentComponent = modalAgent.getContentComponentRef().instance as TestVaryServiceCustomComponent; + const contentComponent = modalAgent.getContentComponent(); const contentElement = contentComponent.elementRef.nativeElement as HTMLElement; // change title from outside const firstButton = modalElement.querySelector('.ant-modal-footer button:first-child') as HTMLButtonElement; @@ -234,7 +234,7 @@ describe('modal', () => { spyOn(logger, 'warn'); const tempModalId = generateUniqueId(); - const modalAgent = instance.createConfirm() as ModalPublicAgent; + const modalAgent = instance.createConfirm() as NzModalRef; const modalElement = modalAgent.getElement(); modalElement.classList.add(tempModalId); fixture.detectChanges(); @@ -295,7 +295,7 @@ describe('modal', () => { - +

content

`, @@ -309,7 +309,7 @@ class NzDemoModalAsyncComponent { this.isVisible = true; } - handleOk($event: MouseEvent): void { + handleOk(): void { this.isOkLoading = true; window.setTimeout(() => { this.isVisible = false; @@ -317,7 +317,7 @@ class NzDemoModalAsyncComponent { }, 3000); } - handleCancel($event: MouseEvent): void { + handleCancel(): void { this.isVisible = false; } } @@ -330,7 +330,7 @@ class NzDemoModalAsyncComponent { styles : [] }) class NzDemoModalConfirmPromiseComponent { - confirmModal: ModalPublicAgent; // For testing by now + confirmModal: NzModalRef; // For testing by now constructor(private modal: NzModalService) { } @@ -349,7 +349,7 @@ class NzDemoModalConfirmPromiseComponent { template: `` }) class TestBasicServiceComponent { - basicModal: ModalPublicAgent; + basicModal: NzModalRef; constructor(private modalService: NzModalService) { this.modalService.create(); // [Testing Required] Only for coverage temporarily @@ -387,14 +387,14 @@ class TestBasicServiceComponent { class TestVaryServiceComponent { constructor(private modalService: NzModalService) {} - createWithVary(): ModalPublicAgent { + createWithVary(): NzModalRef { const modal = this.modalService.create({ nzContent: TestVaryServiceCustomComponent, nzComponentParams: { title: 'internal title', subtitle: 'subtitle' }, nzFooter: [ { label: 'change title from outside', - onClick: (componentInstance: TestVaryServiceCustomComponent) => { + onClick: (componentInstance) => { componentInstance.title = 'internal title changed'; return Promise.resolve(); } @@ -419,7 +419,7 @@ export class TestVaryServiceCustomComponent { @Input() title: string; @Input() subtitle: string; - constructor(private modal: ModalPublicAgent, public elementRef: ElementRef) { } + constructor(private modal: NzModalRef, public elementRef: ElementRef) { } destroyModal(): void { this.modal.destroy(); @@ -432,7 +432,7 @@ export class TestVaryServiceCustomComponent { export class TestConfirmModalComponent { constructor(public modalService: NzModalService) { } - createConfirm(): ModalPublicAgent { + createConfirm(): NzModalRef { this.modalService.confirm(); // [Testing Required] Only for coverage temporarily this.modalService.confirm({ nzWidth: 100 }); // [Testing Required] Only for coverage temporarily diff --git a/components/modal/nz-modal.type.ts b/components/modal/nz-modal.type.ts index 3a99bf2673c..080ff0821e3 100644 --- a/components/modal/nz-modal.type.ts +++ b/components/modal/nz-modal.type.ts @@ -1,13 +1,13 @@ import { EventEmitter, TemplateRef, Type } from '@angular/core'; -export type OnClickCallback = (($event: MouseEvent) => (false | void | {}) | Promise); +export type OnClickCallback = ((instance: T) => (false | void | {}) | Promise); export type ModalType = 'default' | 'confirm'; // Different modal styles we have supported export type ConfirmType = 'confirm' | 'info' | 'success' | 'error' | 'warning'; // Subtypes of Confirm Modal // Public options for using by service -export interface ModalOptions { +export interface ModalOptions { // tslint:disable-line:no-any nzModalType?: ModalType; nzVisible?: boolean; nzZIndex?: number; @@ -17,33 +17,34 @@ export interface ModalOptions { nzStyle?: object; nzIconType?: string; // Confirm Modal ONLY nzTitle?: string | TemplateRef<{}>; - nzContent?: string | TemplateRef<{}> | Type<{}>; + nzContent?: string | TemplateRef<{}> | Type; nzComponentParams?: object; nzClosable?: boolean; nzMask?: boolean; nzMaskClosable?: boolean; nzMaskStyle?: object; nzBodyStyle?: object; - nzFooter?: string | TemplateRef<{}> | ModalButtonOptions[]; // Default Modal ONLY + nzFooter?: string | TemplateRef<{}> | Array>; // Default Modal ONLY nzGetContainer?: HTMLElement | (() => HTMLElement); // STATIC - nzAfterClose?: EventEmitter; + nzAfterClose?: EventEmitter; // --- Predefined OK & Cancel buttons nzOkText?: string; nzOkType?: string; nzOkLoading?: boolean; - nzOnOk?: EventEmitter | OnClickCallback; // Mixed using ng's Input/Output (Should care of "this" when using OnClickCallback) + nzOnOk?: EventEmitter | OnClickCallback; // Mixed using ng's Input/Output (Should care of "this" when using OnClickCallback) nzCancelText?: string; nzCancelLoading?: boolean; - nzOnCancel?: EventEmitter | OnClickCallback; // Mixed using ng's Input/Output (Should care of "this" when using OnClickCallback) + nzOnCancel?: EventEmitter | OnClickCallback; // Mixed using ng's Input/Output (Should care of "this" when using OnClickCallback) } -export interface ModalOptionsForService extends ModalOptions { // Limitations for using by service - nzOnOk?: OnClickCallback; - nzOnCancel?: OnClickCallback; +// tslint:disable-next-line:no-any +export interface ModalOptionsForService extends ModalOptions { // Limitations for using by service + nzOnOk?: OnClickCallback; + nzOnCancel?: OnClickCallback; } -export interface ModalButtonOptions { +export interface ModalButtonOptions { // tslint:disable-line:no-any label: string; type?: string; shape?: string; @@ -52,8 +53,8 @@ export interface ModalButtonOptions { autoLoading?: boolean; // Default: true, indicate whether show loading automatically while onClick returned a Promise // [NOTE] "componentInstance" will refer to the component's instance when using Component - show?: boolean | ((this: ModalButtonOptions, contentComponentInstance?: object) => boolean); - loading?: boolean | ((this: ModalButtonOptions, contentComponentInstance?: object) => boolean); // This prop CAN'T use with autoLoading=true - disabled?: boolean | ((this: ModalButtonOptions, contentComponentInstance?: object) => boolean); - onClick?(this: ModalButtonOptions, contentComponentInstance?: object): (void | {}) | Promise<(void | {})>; + show?: boolean | ((this: ModalButtonOptions, contentComponentInstance?: T) => boolean); + loading?: boolean | ((this: ModalButtonOptions, contentComponentInstance?: T) => boolean); // This prop CAN'T use with autoLoading=true + disabled?: boolean | ((this: ModalButtonOptions, contentComponentInstance?: T) => boolean); + onClick?(this: ModalButtonOptions, contentComponentInstance?: T): (void | {}) | Promise<(void | {})>; } diff --git a/components/modal/public-api.ts b/components/modal/public-api.ts index e009a4bf342..e6e1bf6ea2c 100644 --- a/components/modal/public-api.ts +++ b/components/modal/public-api.ts @@ -1,5 +1,5 @@ export { NzModalComponent } from './nz-modal.component'; -export { ModalPublicAgent } from './modal-public-agent.class'; +export { NzModalRef } from './nz-modal-ref.class'; export { NzModalModule } from './nz-modal.module'; export { NzModalService } from './nz-modal.service'; export * from './nz-modal.type';