diff --git a/src/components/ng-zorro-antd.module.ts b/src/components/ng-zorro-antd.module.ts index 5c5ca2cd6ef..5791d3a6677 100644 --- a/src/components/ng-zorro-antd.module.ts +++ b/src/components/ng-zorro-antd.module.ts @@ -108,6 +108,7 @@ export { NzModalSubject } from './modal/nz-modal-subject.service'; // Tokens (eg. global services' config) export { NZ_MESSAGE_CONFIG } from './message/nz-message-config'; export { NZ_NOTIFICATION_CONFIG } from './notification/nz-notification-config'; +export { NZ_ROOT_CONFIG, NzRootConfig } from './root/nz-root-config'; // --------------------------------------------------------- // | Root module diff --git a/src/components/root/nz-root-config.ts b/src/components/root/nz-root-config.ts new file mode 100644 index 00000000000..34083f432ce --- /dev/null +++ b/src/components/root/nz-root-config.ts @@ -0,0 +1,32 @@ +import { InjectionToken } from '@angular/core'; + +export interface NzRootConfig { + extraFontName: string; + extraFontUrl: string; +} + +export const NZ_ROOT_CONFIG = new InjectionToken('NzRootConfig'); + +export function createNzRootInitializer(document: Document, options?: NzRootConfig) { + return () => { + if (options) { + const style = this._document.createElement('style'); + style.innerHTML = ` + @font-face { + font-family: '${options.extraFontName}'; + src: url('${options.extraFontUrl}.eot'); /* IE9*/ + src: + /* IE6-IE8 */ + url('${options.extraFontUrl}.eot?#iefix') format('embedded-opentype'), + /* chrome、firefox */ + url('${options.extraFontUrl}.woff') format('woff'), + /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/ + url('${options.extraFontUrl}.ttf') format('truetype'), + /* iOS 4.1- */ + url('${options.extraFontUrl}.svg#iconfont') format('svg'); + } + `; + document.head.appendChild(style); + } + } +} diff --git a/src/components/root/nz-root-style.component.ts b/src/components/root/nz-root-style.component.ts new file mode 100644 index 00000000000..799536d13c5 --- /dev/null +++ b/src/components/root/nz-root-style.component.ts @@ -0,0 +1,11 @@ +import { Component, ViewEncapsulation } from '@angular/core'; + +@Component({ + template : ``, + styleUrls : [ + '../style/index.less', + './style/index.less', + ], + encapsulation : ViewEncapsulation.None, +}) +export class NzRootStyleComponent { } diff --git a/src/components/root/nz-root.component.ts b/src/components/root/nz-root.component.ts index a9163a0b2a1..fec92a0ada2 100644 --- a/src/components/root/nz-root.component.ts +++ b/src/components/root/nz-root.component.ts @@ -3,20 +3,17 @@ import { Input, ViewEncapsulation, OnInit, - Inject + Inject, + Optional, } from '@angular/core'; import { DOCUMENT } from '@angular/common'; +import { NZ_ROOT_CONFIG, NzRootConfig, createNzRootInitializer} from './nz-root-config'; @Component({ - selector : '[nz-root],nz-root', - encapsulation: ViewEncapsulation.None, - template : ` + selector : '[nz-root],nz-root', + template : ` `, - styleUrls : [ - '../style/index.less', - './style/index.less', - ] }) export class NzRootComponent implements OnInit { @@ -24,27 +21,17 @@ export class NzRootComponent implements OnInit { @Input() nzExtraFontName: string; @Input() nzExtraFontUrl: string; - constructor(@Inject(DOCUMENT) private _document: Document) { } + constructor( + @Inject(DOCUMENT) private _document: Document, + // Cannot use type annotation here due to https://github.com/angular/angular-cli/issues/2034 + // Should be revisited after AOT being made the only option + @Inject(NZ_ROOT_CONFIG) @Optional() private options: any, + ) { } ngOnInit() { - if (this.nzExtraFontName && this.nzExtraFontUrl) { - const style = this._document.createElement('style'); - style.innerHTML = ` - @font-face { - font-family: '${this.nzExtraFontName}'; - src: url('${this.nzExtraFontUrl}.eot'); /* IE9*/ - src: - /* IE6-IE8 */ - url('${this.nzExtraFontUrl}.eot?#iefix') format('embedded-opentype'), - /* chrome、firefox */ - url('${this.nzExtraFontUrl}.woff') format('woff'), - /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/ - url('${this.nzExtraFontUrl}.ttf') format('truetype'), - /* iOS 4.1- */ - url('${this.nzExtraFontUrl}.svg#iconfont') format('svg'); - } - `; - this._document.head.appendChild(style); + if (this.nzExtraFontName && this.nzExtraFontUrl && !this.options) { + const options: NzRootConfig = { extraFontName: this.nzExtraFontName, extraFontUrl: this.nzExtraFontUrl }; + const initializer = createNzRootInitializer(this._document, options); } } } diff --git a/src/components/root/nz-root.module.spec.ts b/src/components/root/nz-root.module.spec.ts new file mode 100644 index 00000000000..9040f7ab322 --- /dev/null +++ b/src/components/root/nz-root.module.spec.ts @@ -0,0 +1,45 @@ +import { ComponentFactoryResolver, Injector, ComponentRef, ComponentFactory } from '@angular/core'; +import { async, inject, TestBed } from '@angular/core/testing'; +import { DOCUMENT } from '@angular/common'; +import { NzRootModule } from './nz-root.module'; +import { NzRootStyleComponent } from './nz-root-style.component'; + +describe('NzRootModule', () => { + let ngModule: NzRootModule; + let mockDocument: Document; + let mockInjector: Injector; + let mockFactoryResolver: ComponentFactoryResolver; + let mockElement: HTMLDivElement; + let mockComponentFactory: ComponentFactory; + let mockComponentRef: ComponentRef; + + beforeEach(() => { + mockDocument = { createElement: () => null } as any; + mockInjector = {} as any; + mockFactoryResolver = { resolveComponentFactory: () => null } as any; + mockElement = {} as any; + mockComponentFactory = { create: () => null } as any; + mockComponentRef = { destroy: () => null } as any; + + spyOn(mockDocument, 'createElement').and.returnValue(mockElement); + spyOn(mockComponentRef, 'destroy'); + spyOn(mockComponentFactory, 'create').and.returnValue(mockComponentRef); + spyOn(mockFactoryResolver, 'resolveComponentFactory').and.returnValue(mockComponentFactory); + }); + + beforeEach(() => { + ngModule = new NzRootModule(mockDocument, mockInjector, mockFactoryResolver); + }); + + it('should create style component when start', () => { + expect(mockDocument.createElement).toHaveBeenCalledWith('div'); + expect(mockFactoryResolver.resolveComponentFactory).toHaveBeenCalledWith(NzRootStyleComponent); + expect(mockComponentFactory.create).toHaveBeenCalledWith(mockInjector, null, mockElement); + }); + + it('should destroy style component when terminate', () => { + ngModule.ngOnDestroy(); + + expect(mockComponentRef.destroy).toHaveBeenCalled(); + }) +}); diff --git a/src/components/root/nz-root.module.ts b/src/components/root/nz-root.module.ts index 24f7e4cf23b..daef7af340c 100644 --- a/src/components/root/nz-root.module.ts +++ b/src/components/root/nz-root.module.ts @@ -1,12 +1,28 @@ -import { NgModule } from '@angular/core'; +import { NgModule, OnDestroy, ComponentRef, ComponentFactoryResolver, Inject, Optional, Injector, APP_INITIALIZER } from '@angular/core'; +import { CommonModule, DOCUMENT } from '@angular/common'; import { NzRootComponent } from './nz-root.component'; -import { CommonModule } from '@angular/common'; +import { NzRootStyleComponent } from './nz-root-style.component'; +import { NZ_ROOT_CONFIG, createNzRootInitializer, NzRootConfig } from './nz-root-config'; @NgModule({ - exports : [ NzRootComponent ], - declarations: [ NzRootComponent ], - imports : [ CommonModule ] + exports : [ NzRootComponent ], + declarations : [ NzRootComponent, NzRootStyleComponent ], + imports : [ CommonModule ], + entryComponents : [ NzRootStyleComponent ], + providers : [ + { provide: APP_INITIALIZER, multi: true, useFactory: createNzRootInitializer, deps: [DOCUMENT, [new Optional(), NZ_ROOT_CONFIG]] }, + ], }) +export class NzRootModule implements OnDestroy { + private styleHostComponent: ComponentRef; -export class NzRootModule { + constructor(@Inject(DOCUMENT) _document: Document, injector: Injector, resolver: ComponentFactoryResolver) { + const componentFactory = resolver.resolveComponentFactory(NzRootStyleComponent); + const div = _document.createElement('div'); + this.styleHostComponent = componentFactory.create(injector, null, div); + } + + ngOnDestroy() { + this.styleHostComponent.destroy(); + } }