Skip to content

Commit

Permalink
feat(module:message,notification): add close event (NG-ZORRO#2952)
Browse files Browse the repository at this point in the history
* feat(module:message, notification): add close event

close NG-ZORRO#2458

* docs: fix Chinese doc not translated

* fix: remove redundant declaration
  • Loading branch information
Wendell authored and Ricbet committed Apr 9, 2020
1 parent 5e6dddc commit 4180b1f
Show file tree
Hide file tree
Showing 15 changed files with 165 additions and 24 deletions.
15 changes: 15 additions & 0 deletions components/message/demo/close.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
order: 4
title:
zh-CN: 结束事件
en-US: Customize duration
---

## zh-CN

可通过订阅 `onClose` 事件在 message 关闭时做出某些操作。以上用例将依次打开三个 message。

## en-US

You can subscribe to `onClose` event to make some operations. This case would open three messages in sequence.

24 changes: 24 additions & 0 deletions components/message/demo/close.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Component } from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd';
import { concatMap } from 'rxjs/operators';

@Component({
selector: 'nz-demo-message-close',
template: `
<button nz-button [nzType]="'default'" (click)="startShowMessages()">Display a sequence of messages</button>
`,
styles : []
})
export class NzDemoMessageCloseComponent {
constructor(private message: NzMessageService) {
}

startShowMessages(): void {
this.message.loading('Action in progress', { nzDuration: 2500 }).onClose.pipe(
concatMap(() => this.message.success('Loading finished', { nzDuration: 2500 }).onClose),
concatMap(() => this.message.info('Loading finished is finished', { nzDuration: 2500 }).onClose)
).subscribe(() => {
console.log('All completed!');
});
}
}
10 changes: 10 additions & 0 deletions components/message/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,13 @@ Methods for destruction are also provided:
| nzMaxStack | The maximum number of messages that can be displayed at the same time | `number` | `8` |
| nzPauseOnHover | Do not remove automatically when mouse is over while setting to `true` | `boolean` | `true` |
| nzAnimate | Whether to turn on animation | `boolean` | `true` |

### NzMessageDataFilled

It's the object that returned when you call `NzMessageService.success` and others.

```ts
export interface NzMessageDataFilled {
onClose: Subject<false>; // It would emit an event when the message is closed
}
```
10 changes: 10 additions & 0 deletions components/message/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,13 @@ title: Message
| nzMaxStack | 同一时间可展示的最大提示数量 | `number` | `8` |
| nzPauseOnHover | 鼠标移上时禁止自动移除 | `boolean` | `true` |
| nzAnimate | 开关动画效果 | `boolean` | `true` |

### NzMessageDataFilled

当你调用 `NzMessageService.success` 或其他方法时会返回该对象。

```ts
export interface NzMessageDataFilled {
onClose: Subject<false>; // 当 message 关闭时它会派发一个事件
}
```
26 changes: 21 additions & 5 deletions components/message/nz-message-container.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Optional, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs';

import { NzMessageConfig, NZ_MESSAGE_CONFIG, NZ_MESSAGE_DEFAULT_CONFIG } from './nz-message-config';
import { NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions';
Expand Down Expand Up @@ -26,34 +27,49 @@ export class NzMessageContainerComponent {
this.config = { ...this.config, ...config };
}

// Create a new message
/**
* Create a new message.
* @param message Parsed message configuration.
*/
createMessage(message: NzMessageDataFilled): void {
if (this.messages.length >= this.config.nzMaxStack) {
this.messages.splice(0, 1);
}
message.options = this._mergeMessageOptions(message.options);
message.onClose = new Subject<boolean>();
this.messages.push(message);
this.cdr.detectChanges();
}

// Remove a message by messageId
removeMessage(messageId: string): void {
/**
* Remove a message by `messageId`.
* @param messageId Id of the message to be removed.
* @param userAction Whether this is closed by user interaction.
*/
removeMessage(messageId: string, userAction: boolean = false): void {
this.messages.some((message, index) => {
if (message.messageId === messageId) {
this.messages.splice(index, 1);
this.cdr.detectChanges();
message.onClose.next(userAction);
message.onClose.complete();
return true;
}
});
}

// Remove all messages
/**
* Remove all messages.
*/
removeMessageAll(): void {
this.messages = [];
this.cdr.detectChanges();
}

// Merge default options and custom message options
/**
* Merge default options and custom message options
* @param options
*/
protected _mergeMessageOptions(options: NzMessageDataOptions): NzMessageDataOptions {
const defaultOptions: NzMessageDataOptions = {
nzDuration : this.config.nzDuration,
Expand Down
6 changes: 3 additions & 3 deletions components/message/nz-message.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ export class NzMessageComponent implements OnInit, OnDestroy {
}

// Remove self
protected _destroy(): void {
protected _destroy(userAction: boolean = false): void {
if (this._options.nzAnimate) {
this.nzMessage.state = 'leave';
this.cdr.detectChanges();
setTimeout(() => this._messageContainer.removeMessage(this.nzMessage.messageId), 200);
setTimeout(() => this._messageContainer.removeMessage(this.nzMessage.messageId, userAction), 200);
} else {
this._messageContainer.removeMessage(this.nzMessage.messageId);
this._messageContainer.removeMessage(this.nzMessage.messageId, userAction);
}
}

Expand Down
23 changes: 16 additions & 7 deletions components/message/nz-message.definitions.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import { Subject } from 'rxjs';

export type NzMessageType = 'success' | 'info' | 'warning' | 'error' | 'loading';

export interface NzMessageDataOptions {
nzDuration?: number;
nzAnimate?: boolean;
nzPauseOnHover?: boolean;
}

// Message data for terminal users
/**
* Message data for terminal users.
*/
export interface NzMessageData {
// TODO: remove the literal parts as it's widened anyway
type?: 'success' | 'info' | 'warning' | 'error' | 'loading' | string;
type?: NzMessageType | string;
content?: string;
}

// Filled version of NzMessageData (includes more private properties)
/**
* Filled version of NzMessageData (includes more private properties).
*/
export interface NzMessageDataFilled extends NzMessageData {
messageId: string; // Service-wide unique id, auto generated
state?: 'enter' | 'leave';
messageId: string;
createdAt: Date;

options?: NzMessageDataOptions;
createdAt: Date; // Auto created
state?: 'enter' | 'leave';
onClose?: Subject<boolean>;
}
10 changes: 5 additions & 5 deletions components/message/nz-message.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { NzMessageConfig } from './nz-message-config';
import { NzMessageContainerComponent } from './nz-message-container.component';
import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions';

let globalCounter = 0; // global ID counter for messages
let globalCounter = 0;

export class NzMessageBaseService<ContainerClass extends NzMessageContainerComponent, MessageData, MessageConfig extends NzMessageConfig> {
protected _container: ContainerClass;
Expand All @@ -16,9 +16,8 @@ export class NzMessageBaseService<ContainerClass extends NzMessageContainerCompo
private injector: Injector,
private cfr: ComponentFactoryResolver,
private appRef: ApplicationRef,
private _idPrefix: string = '') {

// this._container = overlay.create().attach(new ComponentPortal(containerClass)).instance;
private _idPrefix: string = ''
) {
this._container = this.createContainer();
}

Expand All @@ -33,7 +32,8 @@ export class NzMessageBaseService<ContainerClass extends NzMessageContainerCompo
createMessage(message: MessageData, options?: NzMessageDataOptions): NzMessageDataFilled {
// TODO: spread on literal has been disallow on latest proposal
const resultMessage: NzMessageDataFilled = {
...(message as {}), ...{
...(message as {}),
...{
messageId: this._generateMessageId(),
options,
createdAt: new Date()
Expand Down
14 changes: 14 additions & 0 deletions components/message/nz-message.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@ describe('NzMessage', () => {
tick(1000);
expect(overlayContainerElement.textContent).toContain('EXISTS');
}));

it('should emit event when message close', fakeAsync(() => {
let onCloseFlag = false;

const msg = messageService.create('loading', 'CLOSE');
msg.onClose.subscribe(() => {
onCloseFlag = true;
});

demoAppFixture.detectChanges();
tick(50000);

expect(onCloseFlag).toBeTruthy();
}));
});

@Component({
Expand Down
12 changes: 11 additions & 1 deletion components/notification/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,14 @@ Methods for destruction are also provided:
| nzAnimate | Whether to turn on animation | `boolean` | `true` |
| nzTop | The top of the notification when it pops up from the top. | `string` | 24px |
| nzBottom | The bottom of the notification when it pops up from the bottom. | `string` | 24px |
| nzPlacement | Popup position, optional `topLeft` `topRight` `bottomLeft` `bottomRight` | `string` | `topRight` |
| nzPlacement | Popup position, optional `topLeft` `topRight` `bottomLeft` `bottomRight` | `string` | `topRight` |

### NzNotificationDataFilled

It's the object that returned when you call `NzNotificationService.success` and others.

```ts
export interface NzNotificationDataFilled {
onClose: Subject<boolean>; // It would emit an event when the notification is closed, and emit a `true` if it's closed by user
}
```
12 changes: 11 additions & 1 deletion components/notification/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,14 @@ subtitle: 通知提醒框
| nzAnimate | 开关动画效果 | `boolean` | `true` |
| nzTop | 消息从顶部弹出时,距离顶部的位置。 | `string` | 24px |
| nzBottom | 消息从底部弹出时,距离底部的位置。 | `string` | 24px |
| nzPlacement | 弹出位置,可选 `topLeft` `topRight` `bottomLeft` `bottomRight` | `string` | `topRight` |
| nzPlacement | 弹出位置,可选 `topLeft` `topRight` `bottomLeft` `bottomRight` | `string` | `topRight` |

### NzNotificationDataFilled

当你调用 `NzNotificationService.success` 或其他方法时会返回该对象。

```ts
export interface NzNotificationDataFilled {
onClose: Subject<boolean>; // 当 notification 关闭时它会派发一个事件,如果为用户手动关闭会派发 `true`
}
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Optional, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs';

import { NzMessageContainerComponent } from '../message/nz-message-container.component';
import { NzNotificationConfig, NZ_NOTIFICATION_CONFIG, NZ_NOTIFICATION_DEFAULT_CONFIG } from './nz-notification-config';
Expand Down Expand Up @@ -34,6 +35,7 @@ export class NzNotificationContainerComponent extends NzMessageContainerComponen
*/
createMessage(notification: NzNotificationDataFilled): void {
notification.options = this._mergeMessageOptions(notification.options);
notification.onClose = new Subject<boolean>();
const key = notification.options.nzKey;
const notificationWithSameKey = this.messages.find(msg => msg.options.nzKey === notification.options.nzKey);
if (key && notificationWithSameKey) {
Expand Down
2 changes: 1 addition & 1 deletion components/notification/nz-notification.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class NzNotificationComponent extends NzMessageComponent {
}

close(): void {
this._destroy();
this._destroy(true);
}

get state(): string {
Expand Down
5 changes: 4 additions & 1 deletion components/notification/nz-notification.definitions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TemplateRef } from '@angular/core';
import { Subject } from 'rxjs';

import { NzMessageData, NzMessageDataOptions } from '../message/nz-message.definitions';

Expand All @@ -21,7 +22,9 @@ export interface NzNotificationDataOptions<T = {}> extends NzMessageDataOptions
// Filled version of NzMessageData (includes more private properties)
export interface NzNotificationDataFilled extends NzNotificationData {
messageId: string; // Service-wide unique id, auto generated
createdAt: Date; // Auto created

state?: 'enter' | 'leave';
options?: NzNotificationDataOptions;
createdAt: Date; // Auto created
onClose?: Subject<boolean>;
}
18 changes: 18 additions & 0 deletions components/notification/nz-notification.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,24 @@ describe('NzNotification', () => {
expect(overlayContainerElement.textContent).toContain('SHOULD NOT CHANGE');
expect(overlayContainerElement.querySelector('.ant-notification-notice-icon-success')).not.toBeNull();
});

it('should receive `true` when it is closed by user', fakeAsync(() => {
let onCloseFlag = false;

messageService.create(null, null, 'close').onClose.subscribe(user => {
if (user) {
onCloseFlag = true;
}
});

demoAppFixture.detectChanges();
tick(1000);
const closeEl = overlayContainerElement.querySelector('.ant-notification-notice-close');
dispatchMouseEvent(closeEl, 'click');
tick(1000);
expect(onCloseFlag).toBeTruthy();
tick(50000);
}));
});

@Component({
Expand Down

0 comments on commit 4180b1f

Please sign in to comment.