From 82ac7c5458ab841f4e9581ab7046e351b0e650f3 Mon Sep 17 00:00:00 2001 From: Wendell Date: Wed, 12 Dec 2018 11:12:26 +0800 Subject: [PATCH] refactor(module:notification,message): refactor (#2613) * refactor(module:notification,message): refactor * feat(module:notification): support update by key * docs: update notification docs * fix: fix lint --- .../message/nz-message-container.component.ts | 16 +++++-- components/message/nz-message.component.ts | 26 +++++----- components/notification/demo/update.md | 15 ++++++ components/notification/demo/update.ts | 28 +++++++++++ components/notification/doc/index.en-US.md | 1 + components/notification/doc/index.zh-CN.md | 3 +- .../nz-notification-container.component.ts | 47 +++++++++++++++++-- .../notification/nz-notification.component.ts | 18 +++---- .../nz-notification.definitions.ts | 1 + .../notification/nz-notification.service.ts | 4 +- .../notification/nz-notification.spec.ts | 15 ++++-- 11 files changed, 132 insertions(+), 42 deletions(-) create mode 100644 components/notification/demo/update.md create mode 100644 components/notification/demo/update.ts diff --git a/components/message/nz-message-container.component.ts b/components/message/nz-message-container.component.ts index 074ce59ff34..80ae97ce240 100644 --- a/components/message/nz-message-container.component.ts +++ b/components/message/nz-message-container.component.ts @@ -1,9 +1,11 @@ -import { Component, Inject, Optional } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Optional, ViewEncapsulation } from '@angular/core'; import { NzMessageConfig, NZ_MESSAGE_CONFIG, NZ_MESSAGE_DEFAULT_CONFIG } from './nz-message-config'; import { NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions'; @Component({ + changeDetection : ChangeDetectionStrategy.OnPush, + encapsulation : ViewEncapsulation.None, selector : 'nz-message-container', preserveWhitespaces: false, templateUrl : './nz-message-container.component.html' @@ -12,8 +14,11 @@ export class NzMessageContainerComponent { messages: NzMessageDataFilled[] = []; config: NzMessageConfig = {}; - constructor(@Optional() @Inject(NZ_MESSAGE_DEFAULT_CONFIG) defaultConfig: NzMessageConfig, - @Optional() @Inject(NZ_MESSAGE_CONFIG) config: NzMessageConfig) { + constructor( + protected cdr: ChangeDetectorRef, + @Optional() @Inject(NZ_MESSAGE_DEFAULT_CONFIG) defaultConfig: NzMessageConfig, + @Optional() @Inject(NZ_MESSAGE_CONFIG) config: NzMessageConfig + ) { this.setConfig({ ...defaultConfig, ...config }); } @@ -28,6 +33,7 @@ export class NzMessageContainerComponent { } message.options = this._mergeMessageOptions(message.options); this.messages.push(message); + this.cdr.detectChanges(); } // Remove a message by messageId @@ -35,6 +41,7 @@ export class NzMessageContainerComponent { this.messages.some((message, index) => { if (message.messageId === messageId) { this.messages.splice(index, 1); + this.cdr.detectChanges(); return true; } }); @@ -43,9 +50,10 @@ export class NzMessageContainerComponent { // Remove all messages removeMessageAll(): void { this.messages = []; + this.cdr.detectChanges(); } - // Merge default options and cutom message options + // Merge default options and custom message options protected _mergeMessageOptions(options: NzMessageDataOptions): NzMessageDataOptions { const defaultOptions: NzMessageDataOptions = { nzDuration : this.config.nzDuration, diff --git a/components/message/nz-message.component.ts b/components/message/nz-message.component.ts index 4fbde0b651a..70d4c6cf594 100644 --- a/components/message/nz-message.component.ts +++ b/components/message/nz-message.component.ts @@ -1,21 +1,12 @@ -import { - animate, - state, - style, - transition, - trigger -} from '@angular/animations'; -import { - Component, - Input, - OnDestroy, - OnInit -} from '@angular/core'; +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { NzMessageContainerComponent } from './nz-message-container.component'; import { NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions'; @Component({ + changeDetection : ChangeDetectionStrategy.OnPush, + encapsulation : ViewEncapsulation.None, selector : 'nz-message', preserveWhitespaces: false, animations : [ @@ -32,7 +23,7 @@ import { NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definiti ]) ]) ], - templateUrl : './nz-message.component.html' + templateUrl : './nz-message.component.html' }) export class NzMessageComponent implements OnInit, OnDestroy { @@ -47,7 +38,10 @@ export class NzMessageComponent implements OnInit, OnDestroy { private _eraseTimingStart: number; private _eraseTTL: number; // Time to live - constructor(private _messageContainer: NzMessageContainerComponent) { + constructor( + private _messageContainer: NzMessageContainerComponent, + protected cdr: ChangeDetectorRef + ) { } ngOnInit(): void { @@ -88,6 +82,7 @@ export class NzMessageComponent implements OnInit, OnDestroy { protected _destroy(): void { if (this._options.nzAnimate) { this.nzMessage.state = 'leave'; + this.cdr.detectChanges(); setTimeout(() => this._messageContainer.removeMessage(this.nzMessage.messageId), 200); } else { this._messageContainer.removeMessage(this.nzMessage.messageId); @@ -108,6 +103,7 @@ export class NzMessageComponent implements OnInit, OnDestroy { private _startEraseTimeout(): void { if (this._eraseTTL > 0) { this._clearEraseTimeout(); // To prevent calling _startEraseTimeout() more times to create more timer + // TODO: `window` should be removed in milestone II this._eraseTimer = window.setTimeout(() => this._destroy(), this._eraseTTL); this._eraseTimingStart = Date.now(); } else { diff --git a/components/notification/demo/update.md b/components/notification/demo/update.md new file mode 100644 index 00000000000..d8626215e72 --- /dev/null +++ b/components/notification/demo/update.md @@ -0,0 +1,15 @@ +--- +order: 7 +title: + zh-CN: 更新消息内容 + en-US: Update Notification Content +--- + +## zh-CN + +可以通过唯一的 `nzKey` 来更新内容。 + +## en-US + +Update content with unique `nzKey`. + diff --git a/components/notification/demo/update.ts b/components/notification/demo/update.ts new file mode 100644 index 00000000000..4c9986a51f9 --- /dev/null +++ b/components/notification/demo/update.ts @@ -0,0 +1,28 @@ +import { Component } from '@angular/core'; +import { NzNotificationService } from 'ng-zorro-antd'; + +@Component({ + selector: 'nz-demo-notification-update', + template: ` + + `, + styles : [] +}) +export class NzDemoNotificationUpdateComponent { + + constructor(private notification: NzNotificationService) { + } + + createAutoUpdatingNotifications(): void { + this.notification.blank('Notification Title', 'Description.', { + nzKey: 'key' + } + ); + + setTimeout(() => { + this.notification.blank('New Title', 'New description', { + nzKey: 'key' + }); + }, 1000); + } +} diff --git a/components/notification/doc/index.en-US.md b/components/notification/doc/index.en-US.md index 2637cdaa271..08de7adc6db 100644 --- a/components/notification/doc/index.en-US.md +++ b/components/notification/doc/index.en-US.md @@ -57,6 +57,7 @@ The parameters that are set by the `options` support are as follows: | Argument | Description | Type | | --- | --- | --- | +| nzKey | The unique identifier of the Notification | string | | nzDuration | Duration (milliseconds), does not disappear when set to 0 | number | | nzPauseOnHover | Do not remove automatically when mouse is over while setting to `true` | boolean | | nzAnimate | Whether to turn on animation | boolean | diff --git a/components/notification/doc/index.zh-CN.md b/components/notification/doc/index.zh-CN.md index 0999946f966..a7094e60211 100644 --- a/components/notification/doc/index.zh-CN.md +++ b/components/notification/doc/index.zh-CN.md @@ -56,7 +56,8 @@ subtitle: 通知提醒框 | 参数 | 说明 | 类型 | | --- | --- | --- | -| nzDuration | 持续时间(毫秒),当设置为0时不消失 | number | +| nzKey | 通知提示的唯一标识符 | string | +| nzDuration | 持续时间(毫秒),当设置为 0 时不消失 | number | | nzPauseOnHover | 鼠标移上时禁止自动移除 | boolean | | nzAnimate | 开关动画效果 | boolean | | nzStyle | 自定义内联样式 | object | diff --git a/components/notification/nz-notification-container.component.ts b/components/notification/nz-notification-container.component.ts index 32ce26c4554..8c34928e044 100644 --- a/components/notification/nz-notification-container.component.ts +++ b/components/notification/nz-notification-container.component.ts @@ -1,19 +1,56 @@ -import { Component, Inject, Optional } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Optional, ViewEncapsulation } from '@angular/core'; import { NzMessageContainerComponent } from '../message/nz-message-container.component'; - import { NzNotificationConfig, NZ_NOTIFICATION_CONFIG, NZ_NOTIFICATION_DEFAULT_CONFIG } from './nz-notification-config'; +import { NzNotificationDataFilled } from './nz-notification.definitions'; @Component({ + changeDetection : ChangeDetectionStrategy.OnPush, + encapsulation : ViewEncapsulation.None, selector : 'nz-notification-container', preserveWhitespaces: false, templateUrl : './nz-notification-container.component.html' }) export class NzNotificationContainerComponent extends NzMessageContainerComponent { + constructor( + cdr: ChangeDetectorRef, + @Optional() @Inject(NZ_NOTIFICATION_DEFAULT_CONFIG) defaultConfig: NzNotificationConfig, + @Optional() @Inject(NZ_NOTIFICATION_CONFIG) config: NzNotificationConfig + ) { + super(cdr, defaultConfig, config); + } - constructor(@Optional() @Inject(NZ_NOTIFICATION_DEFAULT_CONFIG) defaultConfig: NzNotificationConfig, - @Optional() @Inject(NZ_NOTIFICATION_CONFIG) config: NzNotificationConfig) { - super(defaultConfig, config); + /** + * A list of notifications displayed on the screen. + * @override + */ + messages: NzNotificationDataFilled[] = []; + + /** + * Create a new notification. + * If there's a notification whose `nzKey` is same with `nzKey` in `NzNotificationDataFilled`, replace its content instead of create a new one. + * @override + * @param notification + */ + createMessage(notification: NzNotificationDataFilled): void { + notification.options = this._mergeMessageOptions(notification.options); + const key = notification.options.nzKey; + const notificationWithSameKey = this.messages.find(msg => msg.options.nzKey === notification.options.nzKey); + if (key && notificationWithSameKey) { + this.replaceNotification(notificationWithSameKey, notification); + } else { + if (this.messages.length >= this.config.nzMaxStack) { + this.messages.splice(0, 1); + } + this.messages.push(notification); + } + this.cdr.detectChanges(); } + private replaceNotification(old: NzNotificationDataFilled, _new: NzNotificationDataFilled): void { + old.title = _new.title; + old.content = _new.content; + old.template = _new.template; + old.type = _new.type; + } } diff --git a/components/notification/nz-notification.component.ts b/components/notification/nz-notification.component.ts index 116f349e614..34546d10c0c 100644 --- a/components/notification/nz-notification.component.ts +++ b/components/notification/nz-notification.component.ts @@ -1,11 +1,5 @@ -import { - animate, - state, - style, - transition, - trigger -} from '@angular/animations'; -import { Component, Input } from '@angular/core'; +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { ChangeDetectorRef, Component, Input, ViewEncapsulation } from '@angular/core'; import { NzMessageComponent } from '../message/nz-message.component'; @@ -13,6 +7,7 @@ import { NzNotificationContainerComponent } from './nz-notification-container.co import { NzNotificationDataFilled } from './nz-notification.definitions'; @Component({ + encapsulation : ViewEncapsulation.None, selector : 'nz-notification', preserveWhitespaces: false, animations : [ @@ -42,13 +37,13 @@ import { NzNotificationDataFilled } from './nz-notification.definitions'; ]) ]) ], - templateUrl : './nz-notification.component.html' + templateUrl : './nz-notification.component.html' }) export class NzNotificationComponent extends NzMessageComponent { @Input() nzMessage: NzNotificationDataFilled; - constructor(private container: NzNotificationContainerComponent) { - super(container); + constructor(private container: NzNotificationContainerComponent, protected cdr: ChangeDetectorRef) { + super(container, cdr); } close(): void { @@ -65,6 +60,5 @@ export class NzNotificationComponent extends NzMessageComponent { } else { return this.nzMessage.state; } - } } diff --git a/components/notification/nz-notification.definitions.ts b/components/notification/nz-notification.definitions.ts index 229ab1c6a7f..6717ee47943 100644 --- a/components/notification/nz-notification.definitions.ts +++ b/components/notification/nz-notification.definitions.ts @@ -10,6 +10,7 @@ export interface NzNotificationData extends NzMessageData { } export interface NzNotificationDataOptions extends NzMessageDataOptions { + nzKey?: string; /* tslint:disable-next-line:no-any */ nzStyle?: any; /* tslint:disable-next-line:no-any */ diff --git a/components/notification/nz-notification.service.ts b/components/notification/nz-notification.service.ts index a593aea46ae..7d753700f49 100644 --- a/components/notification/nz-notification.service.ts +++ b/components/notification/nz-notification.service.ts @@ -16,8 +16,8 @@ export class NzNotificationService extends NzMessageBaseService { beforeEach(fakeAsync(() => { TestBed.configureTestingModule({ - imports: [ NzNotificationModule, NoopAnimationsModule ], + imports : [ NzNotificationModule, NoopAnimationsModule ], declarations: [ DemoAppComponent ], - providers: [ { provide: NZ_NOTIFICATION_CONFIG, useValue: { nzMaxStack: 2 } } ] // Override default config + providers : [ { provide: NZ_NOTIFICATION_CONFIG, useValue: { nzMaxStack: 2 } } ] // Override default config }); TestBed.compileComponents(); @@ -165,6 +164,16 @@ describe('NzNotification', () => { demoAppFixture.detectChanges(); expect(overlayContainerElement.textContent).toContain('test template content'); }); + + it('should update an existing notification when keys are matched', () => { + messageService.create(null, null, 'EXISTS', { nzKey: 'exists' }); + expect(overlayContainerElement.textContent).toContain('EXISTS'); + messageService.create('success', 'Title', 'SHOULD NOT CHANGE', { nzKey: 'exists' }); + expect(overlayContainerElement.textContent).not.toContain('EXISTS'); + expect(overlayContainerElement.textContent).toContain('Title'); + expect(overlayContainerElement.textContent).toContain('SHOULD NOT CHANGE'); + expect(overlayContainerElement.querySelector('.ant-notification-notice-icon-success')).not.toBeNull(); + }); }); @Component({