diff --git a/.travis.yml b/.travis.yml index 0bb66fdd5..09ea3d370 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,4 +61,4 @@ deploy: api_key: secure: J88MqLAoZStZZ77AAf+wgaoZp+8zG3fOUHRneSe4x/yEzyUShS9SlGuq0TSkm9sJVX94iHJl1BQ4yjLshOPV9dkOg1+BB4PbsDTKPCAhPCZgpW7WKz6iImmuWHArchLIRtI1fp+UYi1+V6c7gLALQPY7qR2QJcDJdq1tdgORAyGySMis95ttVhnn6DWTBbs/ocu+IzgOyBSkIiZR0mGk7q/pmVQPy+XL5PQoyUOhD4MmvAAIeVr+XoZ5I8pAUwhi1/bZijXrzWe7LbXh8pTDlEWvYduzYYjJZqUrHiE/e1e8/DIPXGaBUQBj7LRxSqqO8AJXGeCg4DF1R9j4CSG5c0pAwQ/U6vOGu8duPEGaoKG5+HlrTav7gI/YbwFA5HKyh1uzQ5trZDJ4mMKUoB1+8/eL2cjLudtyBB2Kg28wH6f78A9mQC1EJcP7Jz3qJTSUyhczIvwSF8/EkD8xmeaoTi2e+4TNgf7pys1cp6c7m7zKZbvVy25lfyAfG1rCF5+rzKj+GnE9mtLaY6VvlKWjyxklh8hfRBC94TZ8K7PH0tmdgk2Jal+OCdm9FDdmNrBSC1G/gPS8PchtffIRprPhNAUfcVpdg0rlQ4dckbGRbB5UBgwHkpoKasaSTx/nO85AiK6USIYOIod19loXUBvN3QyHUX76w265UhmTnb8iojo= on: - branch: master + branch: master \ No newline at end of file diff --git a/nativescript-angular/directives/dialogs.ts b/nativescript-angular/directives/dialogs.ts index 8953aa3d8..8012cf1d1 100644 --- a/nativescript-angular/directives/dialogs.ts +++ b/nativescript-angular/directives/dialogs.ts @@ -1,9 +1,17 @@ import { - ReflectiveInjector, ComponentFactoryResolver, ViewContainerRef, - Type, Injectable, ComponentRef, Directive + ComponentFactoryResolver, + ComponentRef, + Directive, + Injectable, + NgModuleRef, + ReflectiveInjector, + Type, + ViewContainerRef, } from "@angular/core"; + import { Page } from "tns-core-modules/ui/page"; import { View } from "tns-core-modules/ui/core/view"; + import { DetachedLoader } from "../common/detached-loader"; import { PageFactory, PAGE_FACTORY } from "../platform-providers"; @@ -11,6 +19,7 @@ export interface ModalDialogOptions { context?: any; fullscreen?: boolean; viewContainerRef?: ViewContainerRef; + moduleRef?: NgModuleRef; } export class ModalDialogParams { @@ -20,42 +29,61 @@ export class ModalDialogParams { } } +interface ShowDialogOptions { + containerRef: ViewContainerRef; + context: any; + doneCallback; + fullscreen: boolean; + pageFactory: PageFactory; + parentPage: Page; + resolver: ComponentFactoryResolver; + type: Type; +} + @Injectable() export class ModalDialogService { - public showModal(type: Type, options: ModalDialogOptions): Promise { - if (!options.viewContainerRef) { + public showModal(type: Type, + {viewContainerRef, moduleRef, context, fullscreen}: ModalDialogOptions + ): Promise { + if (!viewContainerRef) { throw new Error( - "No viewContainerRef: Make sure you pass viewContainerRef in ModalDialogOptions."); + "No viewContainerRef: " + + "Make sure you pass viewContainerRef in ModalDialogOptions." + ); } - const viewContainerRef = options.viewContainerRef; const parentPage: Page = viewContainerRef.injector.get(Page); - const resolver: ComponentFactoryResolver = viewContainerRef.injector.get( - ComponentFactoryResolver); const pageFactory: PageFactory = viewContainerRef.injector.get(PAGE_FACTORY); - return new Promise((resolve) => { - setTimeout(() => ModalDialogService.showDialog( - type, - options, - resolve, - viewContainerRef, - resolver, + // resolve from particular module (moduleRef) + // or from same module as parentPage (viewContainerRef) + const componentContainer = moduleRef || viewContainerRef; + const resolver = componentContainer.injector.get(ComponentFactoryResolver); + + return new Promise(resolve => { + setTimeout(() => ModalDialogService.showDialog({ + containerRef: viewContainerRef, + context, + doneCallback: resolve, + fullscreen, + pageFactory, parentPage, - pageFactory - ), 10); + resolver, + type, + }), 10); }); } - private static showDialog( - type: Type, - options: ModalDialogOptions, + private static showDialog({ + containerRef, + context, doneCallback, - containerRef: ViewContainerRef, - resolver: ComponentFactoryResolver, - parentPage: Page, - pageFactory: PageFactory): void { - + fullscreen, + pageFactory, + parentPage, + resolver, + type, + }: ShowDialogOptions): void { const page = pageFactory({ isModal: true, componentType: type }); let detachedLoaderRef: ComponentRef; @@ -66,7 +94,7 @@ export class ModalDialogService { detachedLoaderRef.destroy(); }; - const modalParams = new ModalDialogParams(options.context, closeCallback); + const modalParams = new ModalDialogParams(context, closeCallback); const providers = ReflectiveInjector.resolve([ { provide: Page, useValue: page }, @@ -85,7 +113,7 @@ export class ModalDialogService { } page.content = componentView; - parentPage.showModal(page, options.context, closeCallback, options.fullscreen); + parentPage.showModal(page, context, closeCallback, fullscreen); }); } } @@ -96,7 +124,9 @@ export class ModalDialogService { }) export class ModalDialogHost { // tslint:disable-line:directive-class-suffix constructor() { - throw new Error("ModalDialogHost is deprecated. Call ModalDialogService.showModal() " + - "by passing ViewContainerRef in the options instead."); + throw new Error("ModalDialogHost is deprecated. " + + "Call ModalDialogService.showModal() " + + "by passing ViewContainerRef in the options instead." + ); } } diff --git a/nativescript-angular/nativescript.module.ts b/nativescript-angular/nativescript.module.ts index 521e926ee..2405ef605 100644 --- a/nativescript-angular/nativescript.module.ts +++ b/nativescript-angular/nativescript.module.ts @@ -8,19 +8,25 @@ import "./polyfills/array"; import "./polyfills/console"; import { CommonModule } from "@angular/common"; -import { NativeScriptRendererFactory } from "./renderer"; -import { DetachedLoader } from "./common/detached-loader"; -import { ModalDialogHost, ModalDialogService } from "./directives/dialogs"; import { ApplicationModule, ErrorHandler, + NO_ERRORS_SCHEMA, + NgModule, RendererFactory2, - NgModule, NO_ERRORS_SCHEMA, + SystemJsNgModuleLoader, } from "@angular/core"; + +import { NativeScriptRendererFactory } from "./renderer"; +import { DetachedLoader } from "./common/detached-loader"; import { - defaultPageProvider, + ModalDialogHost, + ModalDialogService, +} from "./directives/dialogs"; +import { + defaultDeviceProvider, defaultFrameProvider, - defaultDeviceProvider + defaultPageProvider, } from "./platform-providers"; import { NS_DIRECTIVES } from "./directives"; @@ -35,13 +41,14 @@ export function errorHandlerFactory() { ...NS_DIRECTIVES, ], providers: [ - { provide: ErrorHandler, useFactory: errorHandlerFactory }, + ModalDialogService, + NativeScriptRendererFactory, + SystemJsNgModuleLoader, + defaultDeviceProvider, defaultFrameProvider, defaultPageProvider, - defaultDeviceProvider, - NativeScriptRendererFactory, + { provide: ErrorHandler, useFactory: errorHandlerFactory }, { provide: RendererFactory2, useClass: NativeScriptRendererFactory }, - ModalDialogService ], entryComponents: [ DetachedLoader, diff --git a/nativescript-angular/router.ts b/nativescript-angular/router.ts index 08bc0c470..c61228382 100644 --- a/nativescript-angular/router.ts +++ b/nativescript-angular/router.ts @@ -4,7 +4,6 @@ import { NO_ERRORS_SCHEMA, Optional, SkipSelf, - SystemJsNgModuleLoader, } from "@angular/core"; import { RouterModule, Routes, ExtraOptions } from "@angular/router"; import { LocationStrategy, PlatformLocation } from "@angular/common"; @@ -38,7 +37,6 @@ export type LocationState = LocationState; NativescriptPlatformLocation, { provide: PlatformLocation, useClass: NativescriptPlatformLocation }, RouterExtensions, - SystemJsNgModuleLoader, ], imports: [ RouterModule, diff --git a/nativescript-angular/router/ns-module-factory-loader.ts b/nativescript-angular/router/ns-module-factory-loader.ts index c08cf5b93..a64868808 100644 --- a/nativescript-angular/router/ns-module-factory-loader.ts +++ b/nativescript-angular/router/ns-module-factory-loader.ts @@ -1,11 +1,11 @@ import { - Injectable, Compiler, + Injectable, NgModuleFactory, NgModuleFactoryLoader, SystemJsNgModuleLoader, + Type, } from "@angular/core"; - import { path, knownFolders } from "tns-core-modules/file-system"; const SEPARATOR = "#"; @@ -14,48 +14,50 @@ const SEPARATOR = "#"; export class NSModuleFactoryLoader implements NgModuleFactoryLoader { private offlineMode: boolean; - constructor(private compiler: Compiler, private ngModuleLoader: SystemJsNgModuleLoader) { + constructor( + private compiler: Compiler, + private ngModuleLoader: SystemJsNgModuleLoader, + ) { this.offlineMode = compiler instanceof Compiler; } load(path: string): Promise> { - if (this.offlineMode) { - return this.ngModuleLoader.load(path); - } else { - return this.loadAndCompile(path); - } + return this.offlineMode ? + this.ngModuleLoader.load(path) : + this.loadAndCompile(path); } private loadAndCompile(path: string): Promise> { - let {modulePath, exportName} = splitPath(path); + const module = requireModule(path); + return Promise.resolve(this.compiler.compileModuleAsync(module)); + } +} - let loadedModule = global.require(modulePath)[exportName]; - checkNotEmpty(loadedModule, modulePath, exportName); +function requireModule(path: string): Type { + const {modulePath, exportName} = splitPath(path); - return Promise.resolve(this.compiler.compileModuleAsync(loadedModule)); - } + const loadedModule = global.require(modulePath)[exportName]; + checkNotEmpty(loadedModule, modulePath, exportName); + return loadedModule; } function splitPath(path: string): {modulePath: string, exportName: string} { - let [modulePath, exportName] = path.split(SEPARATOR); - modulePath = getAbsolutePath(modulePath); - - if (typeof exportName === "undefined") { - exportName = "default"; - } + const [relativeModulePath, exportName = "default"] = path.split(SEPARATOR); + const absoluteModulePath = getAbsolutePath(relativeModulePath); - return {modulePath, exportName}; + return {modulePath: absoluteModulePath, exportName}; } function getAbsolutePath(relativePath: string) { - return path.normalize(path.join(knownFolders.currentApp().path, relativePath)); + const projectPath = knownFolders.currentApp().path; + const absolutePath = path.join(projectPath, relativePath); + + return path.normalize(absolutePath); } -function checkNotEmpty(value: any, modulePath: string, exportName: string): any { +function checkNotEmpty(value: any, modulePath: string, exportName: string): void { if (!value) { throw new Error(`Cannot find '${exportName}' in '${modulePath}'`); } - - return value; } diff --git a/ng-sample/package.json b/ng-sample/package.json index c04b4319c..c1f91caf3 100644 --- a/ng-sample/package.json +++ b/ng-sample/package.json @@ -60,4 +60,4 @@ "scripts": { "tslint": "tslint --project tsconfig.json --config tslint.json" } -} +} \ No newline at end of file diff --git a/tests/app/lazy-loaded.module.ts b/tests/app/lazy-loaded.module.ts index b3b60b401..13ac24300 100644 --- a/tests/app/lazy-loaded.module.ts +++ b/tests/app/lazy-loaded.module.ts @@ -1,19 +1,42 @@ -import { NgModule } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; import { NativeScriptModule } from "nativescript-angular/nativescript.module"; import { NativeScriptRouterModule } from "nativescript-angular/router"; +import { ModalDialogParams } from "nativescript-angular/directives/dialogs"; +import { Page } from "ui/page"; +import { lazyLoadHooksLogProvider } from "./main"; import { SecondComponent } from "./second.component"; const routes = [ { path: ":id", component: SecondComponent }, ]; +@Component({ + selector: "modal-lazy-comp", + template: `` +}) +export class ModalLazyComponent { + constructor(public params: ModalDialogParams, private page: Page) { + page.on("shownModally", () => { + const result = this.params.context; + this.params.closeCallback(result); + }); + } +} + @NgModule({ - declarations: [SecondComponent], + declarations: [ + SecondComponent, + ModalLazyComponent + ], + entryComponents: [ModalLazyComponent], // when lazily loaded and opened via modal on demand imports: [ NativeScriptModule, NativeScriptRouterModule, NativeScriptRouterModule.forChild(routes) + ], + providers: [ + lazyLoadHooksLogProvider ] }) export class SecondModule { } diff --git a/tests/app/main.ts b/tests/app/main.ts index a35eaf68e..ce33b3f50 100644 --- a/tests/app/main.ts +++ b/tests/app/main.ts @@ -49,7 +49,7 @@ const singlePageHooksLogProvider = { provide: HOOKS_LOG, useValue: singlePageHoo const multiPageHooksLog = new BehaviorSubject([]); const multiPageHooksLogProvider = { provide: HOOKS_LOG, useValue: multiPageHooksLog }; const lazyLoadHooksLog = new BehaviorSubject([]); -const lazyLoadHooksLogProvider = { provide: HOOKS_LOG, useValue: lazyLoadHooksLog }; +export const lazyLoadHooksLogProvider = { provide: HOOKS_LOG, useValue: lazyLoadHooksLog }; @NgModule({ bootstrap: [ diff --git a/tests/app/snippets/icon-font.component.html b/tests/app/snippets/icon-font.component.html index ce804bf83..58933b150 100644 --- a/tests/app/snippets/icon-font.component.html +++ b/tests/app/snippets/icon-font.component.html @@ -1,12 +1,12 @@ - + diff --git a/tests/app/snippets/list-view/template-selector.component.html b/tests/app/snippets/list-view/template-selector.component.html index 065756798..75d346c1f 100644 --- a/tests/app/snippets/list-view/template-selector.component.html +++ b/tests/app/snippets/list-view/template-selector.component.html @@ -1,11 +1,11 @@ - + - + \ No newline at end of file diff --git a/tests/app/tests/list-view-tests.ts b/tests/app/tests/list-view-tests.ts index 6dfae0c74..20d4bf1d6 100644 --- a/tests/app/tests/list-view-tests.ts +++ b/tests/app/tests/list-view-tests.ts @@ -26,9 +26,9 @@ let testTemplates: { first: number, second: number }; template: ` - + ` @@ -60,12 +60,12 @@ export class ItemTemplateComponent { template: ` - - + `