diff --git a/components/core/logger/logger.ts b/components/core/logger/logger.ts index 8be6dcb100d..d75798a1a9b 100644 --- a/components/core/logger/logger.ts +++ b/components/core/logger/logger.ts @@ -7,10 +7,10 @@ */ // tslint:disable:no-any -import { environment } from '../environments/environment'; - import { isDevMode } from '@angular/core'; +import { environment } from '../environments/environment'; + const record: Record = {}; export const PREFIX = '[NG-ZORRO]:'; diff --git a/components/core/services/nz-singleton.service.ts b/components/core/services/nz-singleton.service.ts new file mode 100644 index 00000000000..8752f20ee2a --- /dev/null +++ b/components/core/services/nz-singleton.service.ts @@ -0,0 +1,63 @@ +/** + * @license + * Copyright Alibaba.com All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +// tslint:disable no-any + +import { Injectable } from '@angular/core'; + +import { environment } from '../environments/environment'; + +interface SingletonRegistryItem { + target: any; +} + +/** + * When running in test, singletons should not be destroyed. So we keep references of singletons + * in this global variable. + */ +const testSingleRegistry = new Map(); + +/** + * Some singletons should have life cycle that is same to Angular's. This service make sure that + * those singletons get destroyed in HMR. + */ +@Injectable({ + providedIn: 'root' +}) +export class NzSingletonService { + private get singletonRegistry(): Map { + return environment.isTestMode ? testSingleRegistry : this._singletonRegistry; + } + + /** + * This registry is used to register singleton in dev mode. + * So that singletons get destroyed when hot module reload happens. + * + * This works in prod mode too but with no specific effect. + */ + private _singletonRegistry = new Map(); + + registerSingletonWithKey(key: string, target: any): void { + const alreadyHave = this.singletonRegistry.has(key); + const item: SingletonRegistryItem = alreadyHave ? this.singletonRegistry.get(key)! : this.withNewTarget(target); + + if (!alreadyHave) { + this.singletonRegistry.set(key, item); + } + } + + getSingletonWithKey(key: string): T | null { + return this.singletonRegistry.has(key) ? (this.singletonRegistry.get(key)!.target as T) : null; + } + + private withNewTarget(target: any): SingletonRegistryItem { + return { + target + }; + } +} diff --git a/components/core/services/public-api.ts b/components/core/services/public-api.ts index d4427e1ee7c..c02d0887582 100644 --- a/components/core/services/public-api.ts +++ b/components/core/services/public-api.ts @@ -10,3 +10,4 @@ export * from './update-host-class.service'; export * from './nz-copy-to-clipboard.service'; export * from './nz-copy-to-clipboard.service.module'; export * from './nz-dom-event.service'; +export * from './nz-singleton.service'; diff --git a/components/message/demo/info.md b/components/message/demo/info.md index 102c1d6182b..d17f5e3c86a 100644 --- a/components/message/demo/info.md +++ b/components/message/demo/info.md @@ -12,4 +12,3 @@ title: ## en-US Normal messages as feedbacks. - diff --git a/components/message/nz-message-base.service.ts b/components/message/nz-message-base.service.ts index 812de44b7b9..1d587058075 100644 --- a/components/message/nz-message-base.service.ts +++ b/components/message/nz-message-base.service.ts @@ -8,13 +8,13 @@ import { Overlay } from '@angular/cdk/overlay'; import { ApplicationRef, ComponentFactoryResolver, EmbeddedViewRef, Injector, Type } from '@angular/core'; +import { NzSingletonService } from 'ng-zorro-antd/core'; import { NzMessageConfig } from './nz-message-config'; import { NzMessageContainerComponent } from './nz-message-container.component'; import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions'; let globalCounter = 0; -const containerMap = new Map(); export class NzMessageBaseService< ContainerClass extends NzMessageContainerComponent, @@ -24,6 +24,7 @@ export class NzMessageBaseService< protected _container: ContainerClass; constructor( + private nzSingletonService: NzSingletonService, private overlay: Overlay, private containerClass: Type, private injector: Injector, @@ -31,8 +32,8 @@ export class NzMessageBaseService< private appRef: ApplicationRef, private name: string = '' ) { - this._container = this.createContainer(); - containerMap.set(this.name, this._container); + this._container = this.withContainer(); + this.nzSingletonService.registerSingletonWithKey(this.name, this._container); } remove(messageId?: string): void { @@ -67,10 +68,13 @@ export class NzMessageBaseService< // Manually creating container for overlay to avoid multi-checking error, see: https://github.com/NG-ZORRO/ng-zorro-antd/issues/391 // NOTE: we never clean up the container component and it's overlay resources, if we should, we need to do it by our own codes. - private createContainer(): ContainerClass { - if (containerMap.has(this.name)) { - return containerMap.get(this.name) as ContainerClass; + private withContainer(): ContainerClass { + const containerInstance = this.nzSingletonService.getSingletonWithKey(this.name); + + if (containerInstance) { + return containerInstance as ContainerClass; } + const factory = this.cfr.resolveComponentFactory(this.containerClass); const componentRef = factory.create(this.injector); // Use root injector componentRef.changeDetectorRef.detectChanges(); // Immediately change detection to avoid multi-checking error diff --git a/components/message/nz-message.service.ts b/components/message/nz-message.service.ts index 9fb1564b75f..b91818b0453 100644 --- a/components/message/nz-message.service.ts +++ b/components/message/nz-message.service.ts @@ -8,6 +8,7 @@ import { Overlay } from '@angular/cdk/overlay'; import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector, TemplateRef } from '@angular/core'; +import { NzSingletonService } from 'ng-zorro-antd/core'; import { NzMessageBaseService } from './nz-message-base.service'; import { NzMessageConfig } from './nz-message-config'; @@ -23,8 +24,14 @@ export class NzMessageService extends NzMessageBaseService< NzMessageData, NzMessageConfig > { - constructor(overlay: Overlay, injector: Injector, cfr: ComponentFactoryResolver, appRef: ApplicationRef) { - super(overlay, NzMessageContainerComponent, injector, cfr, appRef, 'message'); + constructor( + nzSingletonService: NzSingletonService, + overlay: Overlay, + injector: Injector, + cfr: ComponentFactoryResolver, + appRef: ApplicationRef + ) { + super(nzSingletonService, overlay, NzMessageContainerComponent, injector, cfr, appRef, 'message'); } // Shortcut methods diff --git a/components/message/nz-message.spec.ts b/components/message/nz-message.spec.ts index 414a71ec1c2..acbd92724ca 100644 --- a/components/message/nz-message.spec.ts +++ b/components/message/nz-message.spec.ts @@ -44,7 +44,6 @@ describe('NzMessage', () => { it('should open a message box with success', () => { messageService.success('SUCCESS'); fixture.detectChanges(); - console.log(overlayContainerElement.textContent); expect((overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement).style.zIndex).toBe('1010'); expect(overlayContainerElement.textContent).toContain('SUCCESS'); diff --git a/components/notification/nz-notification.service.ts b/components/notification/nz-notification.service.ts index 7054112694b..87ef8c12aaf 100644 --- a/components/notification/nz-notification.service.ts +++ b/components/notification/nz-notification.service.ts @@ -9,6 +9,7 @@ import { Overlay } from '@angular/cdk/overlay'; import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector, TemplateRef } from '@angular/core'; +import { NzSingletonService } from 'ng-zorro-antd/core'; import { NzMessageBaseService } from 'ng-zorro-antd/message'; import { NzNotificationConfig } from './nz-notification-config'; @@ -24,8 +25,14 @@ export class NzNotificationService extends NzMessageBaseService< NzNotificationData, NzNotificationConfig > { - constructor(overlay: Overlay, injector: Injector, cfr: ComponentFactoryResolver, appRef: ApplicationRef) { - super(overlay, NzNotificationContainerComponent, injector, cfr, appRef, 'notification-'); + constructor( + nzSingletonService: NzSingletonService, + overlay: Overlay, + injector: Injector, + cfr: ComponentFactoryResolver, + appRef: ApplicationRef + ) { + super(nzSingletonService, overlay, NzNotificationContainerComponent, injector, cfr, appRef, 'notification-'); } // Shortcut methods