Skip to content

Commit

Permalink
fix(module:message): fix container instance not destroyed in HMR (#3859)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wendell authored Aug 9, 2019
1 parent 8081cdf commit 07e86a5
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 14 deletions.
4 changes: 2 additions & 2 deletions components/core/logger/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, boolean> = {};

export const PREFIX = '[NG-ZORRO]:';
Expand Down
63 changes: 63 additions & 0 deletions components/core/services/nz-singleton.service.ts
Original file line number Diff line number Diff line change
@@ -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<string, SingletonRegistryItem>();

/**
* 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<string, SingletonRegistryItem> {
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<string, SingletonRegistryItem>();

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<T>(key: string): T | null {
return this.singletonRegistry.has(key) ? (this.singletonRegistry.get(key)!.target as T) : null;
}

private withNewTarget(target: any): SingletonRegistryItem {
return {
target
};
}
}
1 change: 1 addition & 0 deletions components/core/services/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
1 change: 0 additions & 1 deletion components/message/demo/info.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ title:
## en-US

Normal messages as feedbacks.

16 changes: 10 additions & 6 deletions components/message/nz-message-base.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, NzMessageContainerComponent>();

export class NzMessageBaseService<
ContainerClass extends NzMessageContainerComponent,
Expand All @@ -24,15 +24,16 @@ export class NzMessageBaseService<
protected _container: ContainerClass;

constructor(
private nzSingletonService: NzSingletonService,
private overlay: Overlay,
private containerClass: Type<ContainerClass>,
private injector: Injector,
private cfr: ComponentFactoryResolver,
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 {
Expand Down Expand Up @@ -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
Expand Down
11 changes: 9 additions & 2 deletions components/message/nz-message.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion components/message/nz-message.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
11 changes: 9 additions & 2 deletions components/notification/nz-notification.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
Expand Down

0 comments on commit 07e86a5

Please sign in to comment.