Skip to content

Commit

Permalink
feat(module:modal): add afterOpen/afterAllClose/closeAll/openModals, …
Browse files Browse the repository at this point in the history
…adjust the boolean props and changeBodyOverflow, complete testing.

close #1162
  • Loading branch information
wilsoncook committed Mar 19, 2018
1 parent f5410cb commit 261bc29
Show file tree
Hide file tree
Showing 12 changed files with 438 additions and 82 deletions.
2 changes: 1 addition & 1 deletion components/core/style/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
.cdk-overlay-pane {
position: absolute;
pointer-events: auto;
z-index: 1000;
// z-index: 1000; // Give an opportunity to the content own to manage their z-index such as Modal
}

.box-shadow-left() {
Expand Down
15 changes: 12 additions & 3 deletions components/modal/demo/basic.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { Component } from '@angular/core';
import { Component, ViewChild, OnInit } from '@angular/core';
import { NzModalComponent } from 'ng-zorro-antd';

@Component({
selector: 'nz-demo-modal-basic',
template: `
<button nz-button [nzType]="'primary'" (click)="showModal()"><span>Show Modal</span></button>
<nz-modal [(nzVisible)]="isVisible" nzTitle="The first Modal" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()">
<nz-modal #modal *ngIf="modalValid" [(nzVisible)]="isVisible" nzTitle="The first Modal" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()">
<p>Content one</p>
<p>Content two</p>
<p>Content three</p>
</nz-modal>
`,
styles: []
})
export class NzDemoModalBasicComponent {
export class NzDemoModalBasicComponent implements OnInit {
isVisible = false;
modalValid = true;

@ViewChild('modal') private modal: NzModalComponent;

constructor() {}

ngOnInit(): void {
(window as any).modal = this.modal; // tslint:disable-line
}

showModal(): void {
this.isVisible = true;
window.setTimeout(() => this.modalValid = false, 2000);
}

handleOk(): void {
Expand Down
29 changes: 27 additions & 2 deletions components/modal/demo/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* entryComponents: NzModalCustomComponent */

import { Component, Input, TemplateRef } from '@angular/core';
import { Component, Input, TemplateRef, ViewChild } from '@angular/core';
import { NzModalRef, NzModalService } from 'ng-zorro-antd';

@Component({
Expand Down Expand Up @@ -34,11 +34,17 @@ import { NzModalRef, NzModalService } from 'ng-zorro-antd';
</button>
<button nz-button nzType="primary" (click)="createCustomButtonModal()">Custom Button</button>
<br /><br />
<button nz-button nzType="primary" (click)="openAndCloseAll()">Open more modals then close all after 2s</button>
<nz-modal [(nzVisible)]="htmlModalVisible" nzMask="false" nzZIndex="1001" nzTitle="Non-service html modal">This is a non-service html modal</nz-modal>
`
})
export class NzDemoModalServiceComponent {
tplModal: NzModalRef;
tplModalButtonLoading = false;
htmlModalVisible = false;

constructor(private modalService: NzModalService) { }

Expand Down Expand Up @@ -86,8 +92,10 @@ export class NzDemoModalServiceComponent {
}]
});

modal.afterOpen.subscribe(() => console.log('[afterOpen] emitted!'));

// Return a result when closed
modal.afterClose().subscribe((result) => console.log('[afterClose] The result is:', result));
modal.afterClose.subscribe((result) => console.log('[afterClose] The result is:', result));

// delay until modal instance created
window.setTimeout(() => {
Expand Down Expand Up @@ -133,6 +141,23 @@ export class NzDemoModalServiceComponent {
]
});
}

openAndCloseAll(): void {
let pos = 0;

[ 'create', 'info', 'success', 'error' ].forEach((method) => this.modalService[method]({
nzMask: false,
nzTitle: `Test ${method} title`,
nzContent: `Test content: <b>${method}</b>`,
nzStyle: { position: 'absolute', top: `${pos * 70}px`, left: `${(pos++) * 300}px` }
}));

this.htmlModalVisible = true;

this.modalService.afterAllClose.subscribe(() => console.log('afterAllClose emitted!'));

window.setTimeout(() => this.modalService.closeAll(), 2000);
}
}

@Component({
Expand Down
12 changes: 8 additions & 4 deletions components/modal/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ The dialog is currently divided into 2 modes, `normal mode` and `confirm box mod

| Property | Description | Type | Default |
|----|----|----|----|
| nzAfterClose | Specify a EventEmitter that will be emitted when modal is closed completely. | EventEmitter | - |
| nzAfterOpen | Specify a EventEmitter that will be emitted when modal opened | EventEmitter | - |
| nzAfterClose | Specify a EventEmitter that will be emitted when modal is closed completely (Can listen for parameters passed in the close/destroy method) | EventEmitter | - |
| nzBodyStyle | Body style for modal body element. Such as height, padding etc. | object | - |
| nzCancelText | Text of the Cancel button. <i>Set to null to show no cancel button (this value is invalid if the nzFooter parameter is used in normal mode)</i> | string | Cancel |
| nzClosable | Whether a close (x) button is visible on top right of the modal dialog or not. <i>Invalid value in confirm box mode (default will be hidden)</i> | boolean | true |
Expand Down Expand Up @@ -81,22 +82,25 @@ All the `NzModalService.method`s will return a reference, and then we can close
```ts
constructor(modal: NzModalService) {
const ref: NzModalRef = modal.info();
ref.destroy(); // Note: This dialog will be destroyed directly
ref.close(); // Or ref.destroy(); This dialog will be destroyed directly
}
```

### Related type definition

#### NzModalRef (used for control dialogs)
#### NzModalRef

> NzModalRef object is used to control dialogs and communicate with inside content
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 |
|----|----|
| afterOpen | Same as nzAfterOpen but of type Observable&lt;void&gt; |
| afterClose | Same as nzAfterClose, but of type Observable&lt;result:any&gt; |
| open() | Open (display) dialog box. <i>Calling this function will fail if the dialog is already destroyed</i> |
| close() | Close (hide) the dialog. <i>Note: When used for a dialog created as a service, this method will destroy the dialog directly (as with the destroy method)</i> |
| destroy() | Destroy the dialog. <i>Note: Used only for dialogs created by the service (non-service created dialogs, this method only hides the dialog)</i> |
| 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`. <i> Note: When the dialog is not initialized (`ngOnInit` is not executed), this function will return `undefined`</i> |

#### ModalButtonOptions (used to customize the bottom button)
Expand Down
22 changes: 17 additions & 5 deletions components/modal/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ title: Modal

| 参数 | 说明 | 类型 | 默认值 |
|----|----|----|----|
| nzAfterClose | Modal 完全关闭后的回调 | EventEmitter ||
| nzAfterOpen | Modal 打开后的回调 | EventEmitter ||
| nzAfterClose | Modal 完全关闭后的回调,可监听close/destroy方法传入的参数 | EventEmitter ||
| nzBodyStyle | Modal body 样式 | object ||
| nzCancelText | 取消按钮文字。<i>设为 null 表示不显示取消按钮(若在普通模式下使用了 nzFooter 参数,则该值无效)</i> | string | 取消 |
| nzClosable | 是否显示右上角的关闭按钮。<i>确认框模式下该值无效(默认会被隐藏)</i> | boolean | true |
Expand Down Expand Up @@ -80,22 +81,33 @@ title: Modal
```ts
constructor(modal: NzModalService) {
const ref: NzModalRef = modal.info();
ref.destroy(); // 注:这里将直接销毁对话框
ref.close(); // 或 ref.destroy(); 将直接销毁对话框
}
```

### 相关类型定义

#### NzModalRef(用于控制对话框)
#### NzModalService的其他方法/属性

| 方法/属性 | 说明 | 类型 |
|----|----|
| openModals | 当前打开的所有Modal引用列表 | NzModalRef[] |
| afterAllClose | 所有Modal完全关闭后的回调 | Observable&lt;void&gt; |
| closeAll() | 关闭所有模态框 | function |

#### NzModalRef

> NzModalRef 对象用于控制对话框以及进行内容间的通信
通过服务方式 `NzModalService.xxx()` 创建的对话框,都会返回一个 `NzModalRef` 对象,用于操控该对话框(若使用nzContent为Component时,也可通过依赖注入 `NzModalRef` 方式获得此对象),该对象具有以下方法:

| 方法 | 说明 |
| 方法/属性 | 说明 |
|----|----|
| afterOpen | 同nzAfterOpen,但类型为Observable&lt;void&gt; |
| afterClose | 同nzAfterClose,但类型为Observable&lt;result:any&gt; |
| open() | 打开(显示)对话框。<i>若对话框已销毁,则调用此函数将失效</i> |
| close(result: any) | 关闭(隐藏)对话框。<i>注:当用于以服务方式创建的对话框,此方法将直接 销毁 对话框(同destroy方法)</i> |
| destroy(result: any) | 销毁对话框。<i>注:仅用于服务方式创建的对话框(非服务方式创建的对话框,此方法只会隐藏对话框)</i> |
| afterClose() | 返回一个Observable对象来获取close/destroy中传递的result参数(将在对话框关闭后触发) |
| getContentComponent() | 获取对话框内容中`nzContent`的Component实例instance。<i>注:当对话框还未初始化完毕(`ngOnInit`未执行)时,此函数将返回`undefined`</i> |

#### ModalButtonOptions(用于自定义底部按钮)
Expand Down
72 changes: 72 additions & 0 deletions components/modal/nz-modal-control.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Injectable, Optional, SkipSelf } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';

import { NzModalRef } from './nz-modal-ref.class';

interface RegisteredMeta {
modalRef: NzModalRef;
afterOpenSubscription: Subscription;
afterCloseSubscription: Subscription;
}

@Injectable()
export class NzModalControlService {
// Track singleton afterAllClose through over the injection tree
get afterAllClose(): Subject<void> {
return this.parentService ? this.parentService.afterAllClose : this.rootAfterAllClose;
}
// Track singleton openModals array through over the injection tree
get openModals(): NzModalRef[] {
return this.parentService ? this.parentService.openModals : this.rootOpenModals;
}

private rootOpenModals: NzModalRef[] = this.parentService ? null : [];
private rootAfterAllClose: Subject<void> = this.parentService ? null : new Subject<void>();

private rootRegisteredMetaMap: Map<NzModalRef, RegisteredMeta> = this.parentService ? null : new Map();
private get registeredMetaMap(): Map<NzModalRef, RegisteredMeta> { // Registered modal for later usage
return this.parentService ? this.parentService.registeredMetaMap : this.rootRegisteredMetaMap;
}

constructor(
@Optional() @SkipSelf() private parentService: NzModalControlService) {}

// Register a modal to listen its open/close
registerModal(modalRef: NzModalRef): void {
if (!this.hasRegistered(modalRef)) {
const afterOpenSubscription = modalRef.afterOpen.subscribe(() => this.openModals.push(modalRef));
const afterCloseSubscription = modalRef.afterClose.subscribe(() => this.removeOpenModal(modalRef));

this.registeredMetaMap.set(modalRef, { modalRef, afterOpenSubscription, afterCloseSubscription });
}
}

// TODO: allow deregister modals
// deregisterModal(modalRef: NzModalRef): void {}

hasRegistered(modalRef: NzModalRef): boolean {
return this.registeredMetaMap.has(modalRef);
}

// Close all registered opened modals
closeAll(): void {
let i = this.openModals.length;

while (i--) {
this.openModals[i].close();
}
}

private removeOpenModal(modalRef: NzModalRef): void {
const index = this.openModals.indexOf(modalRef);

if (index > -1) {
this.openModals.splice(index, 1);

if (!this.openModals.length) {
this.afterAllClose.next();
}
}
}
}
4 changes: 3 additions & 1 deletion components/modal/nz-modal-ref.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import { NzModalComponent } from './nz-modal.component';
* NzModalRef is aim to avoid accessing to the modal instance directly by users.
*/
export abstract class NzModalRef<T = any, R = any> { // tslint:disable-line:no-any
abstract afterOpen: Observable<void>;
abstract afterClose: Observable<R>;

abstract open(): void;
abstract close(result?: R): void;
abstract destroy(result?: R): void;
abstract afterClose(): Observable<R | undefined>;

// /**
// * Return the ComponentRef of nzContent when specify nzContent as a Component
Expand Down
Loading

0 comments on commit 261bc29

Please sign in to comment.