diff --git a/nativescript-angular/nativescript.module.ts b/nativescript-angular/nativescript.module.ts index dc0790865..4ce3523e2 100644 --- a/nativescript-angular/nativescript.module.ts +++ b/nativescript-angular/nativescript.module.ts @@ -17,7 +17,9 @@ import { NgModule, NO_ERRORS_SCHEMA, } from "@angular/core"; import { - defaultPageProvider, defaultFrameProvider, defaultDeviceProvider + defaultPageProvider, + defaultFrameProvider, + defaultDeviceProvider } from "./platform-providers"; import { NS_DIRECTIVES } from "./directives"; @@ -36,6 +38,7 @@ export function errorHandlerFactory() { defaultFrameProvider, defaultPageProvider, defaultDeviceProvider, + NativeScriptRootRenderer, { provide: RootRenderer, useClass: NativeScriptRootRenderer }, NativeScriptRenderer, diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts index 168a4946c..a1ebaaa48 100644 --- a/nativescript-angular/platform-common.ts +++ b/nativescript-angular/platform-common.ts @@ -15,7 +15,7 @@ import { EventEmitter, Provider, Sanitizer, - OpaqueToken, + OpaqueToken } from "@angular/core"; // Work around a TS bug requiring an import of OpaqueToken without using it diff --git a/nativescript-angular/router.ts b/nativescript-angular/router.ts index 2a8c10c47..8be260fb1 100644 --- a/nativescript-angular/router.ts +++ b/nativescript-angular/router.ts @@ -1,4 +1,4 @@ -import { NgModule, ModuleWithProviders, NO_ERRORS_SCHEMA } from "@angular/core"; +import { NgModule, ModuleWithProviders, NO_ERRORS_SCHEMA, NgModuleFactoryLoader } from "@angular/core"; import { RouterModule, Routes, ExtraOptions } from "@angular/router"; import { LocationStrategy, PlatformLocation } from "@angular/common"; import { NSRouterLink } from "./router/ns-router-link"; @@ -11,6 +11,7 @@ export { routerTraceCategory } from "./trace"; export { PageRoute } from "./router/page-router-outlet"; export { RouterExtensions } from "./router/router-extensions"; import { NativeScriptModule } from "./nativescript.module"; +import { NsModuleFactoryLoader } from "./router/ns-module-factory-loader"; @NgModule({ declarations: [ @@ -39,7 +40,14 @@ import { NativeScriptModule } from "./nativescript.module"; }) export class NativeScriptRouterModule { static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders { - return RouterModule.forRoot(routes, config); + let moduleWithProviders = RouterModule.forRoot(routes, config); + + // Override the stock SystemJsNgModuleLoader + moduleWithProviders.providers.push( + { provide: NgModuleFactoryLoader, useClass: NsModuleFactoryLoader }, + ); + + return moduleWithProviders; } static forChild(routes: Routes): ModuleWithProviders { diff --git a/nativescript-angular/router/ns-module-factory-loader.ts b/nativescript-angular/router/ns-module-factory-loader.ts new file mode 100644 index 000000000..17fa0d97d --- /dev/null +++ b/nativescript-angular/router/ns-module-factory-loader.ts @@ -0,0 +1,82 @@ +import { + Injectable, + Compiler, + NgModuleFactory, + NgModuleFactoryLoader +} from "@angular/core"; + +import { path, knownFolders } from "file-system"; + +declare var System: any; +const SEPARATOR = "#"; +const FACTORY_CLASS_SUFFIX = "NgFactory"; +const FACTORY_PATH_SUFFIX = ".ngfactory"; + +@Injectable() +export class NsModuleFactoryLoader implements NgModuleFactoryLoader { + private offlineMode: boolean; + + constructor(private compiler: Compiler) { + this.offlineMode = compiler instanceof Compiler; + } + + load(path: string): Promise> { + let {modulePath, exportName} = this.splitPath(path); + + if (this.offlineMode) { + return this.loadFactory(modulePath, exportName); + } else { + return this.loadAndCompile(modulePath, exportName); + } + } + + private loadFactory(modulePath: string, exportName: string): Promise> { + modulePath = factoryModulePath(modulePath); + exportName = factoryExportName(exportName); + + return System.import(modulePath) + .then((module: any) => module[exportName]) + .then((factory: any) => checkNotEmpty(factory, modulePath, exportName)); + } + + private loadAndCompile(modulePath: string, exportName: string): Promise> { + modulePath = getAbsolutePath(modulePath); + + let loadedModule = require(modulePath)[exportName]; + checkNotEmpty(loadedModule, modulePath, exportName); + + return Promise.resolve(this.compiler.compileModuleAsync(loadedModule)); + } + + private splitPath(path: string): {modulePath: string, exportName: string} { + let [modulePath, exportName] = path.split(SEPARATOR); + + if (typeof exportName === "undefined") { + exportName = "default"; + } + + return {modulePath, exportName}; + } +} + +function getAbsolutePath(relativePath: string) { + return path.normalize(path.join(knownFolders.currentApp().path, relativePath)); +} + +function factoryModulePath(modulePath) { + return `${modulePath}${FACTORY_PATH_SUFFIX}`; +} + +function factoryExportName(exportName) { + return exportName === "default" ? + exportName : + `${exportName}${FACTORY_CLASS_SUFFIX}`; +} + +function checkNotEmpty(value: any, modulePath: string, exportName: string): any { + if (!value) { + throw new Error(`Cannot find '${exportName}' in '${modulePath}'`); + } + + return value; +} diff --git a/tests/app/lazy-load-main.ts b/tests/app/lazy-load-main.ts index f7ac1fb02..71a17f2b4 100644 --- a/tests/app/lazy-load-main.ts +++ b/tests/app/lazy-load-main.ts @@ -13,5 +13,5 @@ export class LazyLoadMain { export const routes = [ { path: "", redirectTo: "first/lazy-load", pathMatch: "full" }, { path: "first/:id", component: FirstComponent }, - { path: "second", loadChildren: () => require("./lazy-loaded.module")["SecondModule"] } + { path: "second", loadChildren: "./lazy-loaded.module#SecondModule" } ]; diff --git a/tests/app/main.ts b/tests/app/main.ts index 64d4b97d9..c5c5535f5 100644 --- a/tests/app/main.ts +++ b/tests/app/main.ts @@ -2,31 +2,31 @@ import { NativeScriptModule, platformNativeScriptDynamic } from "nativescript-angular/platform"; import { NativeScriptRouterModule } from "nativescript-angular/router"; import { NativeScriptFormsModule } from "nativescript-angular/forms"; -import {AppComponent} from "./app.component"; -import {GestureComponent} from "./snippets/gestures.component"; -import {LayoutsComponent} from "./snippets/layouts.component"; -import {IconFontComponent} from "./snippets/icon-font.component"; -import {APP_ROOT_VIEW} from "nativescript-angular/platform-providers"; -import {Page} from "ui/page"; -import {StackLayout} from "ui/layouts/stack-layout"; +import { AppComponent } from "./app.component"; +import { GestureComponent } from "./snippets/gestures.component"; +import { LayoutsComponent } from "./snippets/layouts.component"; +import { IconFontComponent } from "./snippets/icon-font.component"; +import { APP_ROOT_VIEW } from "nativescript-angular/platform-providers"; +import { Page } from "ui/page"; +import { StackLayout } from "ui/layouts/stack-layout"; import * as application from "application"; import "ui/styling/style"; import "ui/frame"; -import {HOOKS_LOG} from "./base.component"; -import {MultiPageMain, routes as multiPageRoutes} from "./multi-page-main.component"; -import {SinglePageMain, routes as singlePageRoutes} from "./single-page-main.component"; -import {LazyLoadMain, routes as lazyLoadRoutes} from "./lazy-load-main"; -import {FirstComponent} from "./first.component"; -import {SecondComponent} from "./second.component"; +import { HOOKS_LOG } from "./base.component"; +import { MultiPageMain, routes as multiPageRoutes } from "./multi-page-main.component"; +import { SinglePageMain, routes as singlePageRoutes } from "./single-page-main.component"; +import { LazyLoadMain, routes as lazyLoadRoutes } from "./lazy-load-main"; +import { FirstComponent } from "./first.component"; +import { SecondComponent } from "./second.component"; import { OpaqueToken, NgModule } from "@angular/core"; -import {PageNavigationApp} from "./snippets/navigation/page-outlet"; -import {NavigationApp} from "./snippets/navigation/router-outlet"; +import { PageNavigationApp } from "./snippets/navigation/page-outlet"; +import { NavigationApp } from "./snippets/navigation/router-outlet"; import { rendererTraceCategory, routerTraceCategory } from "nativescript-angular/trace"; -import {BehaviorSubject} from "rxjs"; +import { BehaviorSubject } from "rxjs"; import trace = require("trace"); // trace.setCategories(rendererTraceCategory + "," + routerTraceCategory); @@ -36,15 +36,15 @@ trace.enable(); // nativeScriptBootstrap(GestureComponent); // nativeScriptBootstrap(LayoutsComponent); // nativeScriptBootstrap(IconFontComponent); -const platform = platformNativeScriptDynamic({bootInExistingPage: true}); +const platform = platformNativeScriptDynamic({ bootInExistingPage: true }); const root = new StackLayout(); -const rootViewProvider = {provide: APP_ROOT_VIEW, useValue: root}; +const rootViewProvider = { provide: APP_ROOT_VIEW, useValue: root }; const singlePageHooksLog = new BehaviorSubject([]); -const singlePageHooksLogProvider = {provide: HOOKS_LOG, useValue: singlePageHooksLog}; +const singlePageHooksLogProvider = { provide: HOOKS_LOG, useValue: singlePageHooksLog }; const multiPageHooksLog = new BehaviorSubject([]); -const multiPageHooksLogProvider = {provide: HOOKS_LOG, useValue: multiPageHooksLog}; +const multiPageHooksLogProvider = { provide: HOOKS_LOG, useValue: multiPageHooksLog }; const lazyLoadHooksLog = new BehaviorSubject([]); -const lazyLoadHooksLogProvider = {provide: HOOKS_LOG, useValue: lazyLoadHooksLog}; +const lazyLoadHooksLogProvider = { provide: HOOKS_LOG, useValue: lazyLoadHooksLog }; @NgModule({ bootstrap: [ @@ -76,7 +76,7 @@ const lazyLoadHooksLogProvider = {provide: HOOKS_LOG, useValue: lazyLoadHooksLog singlePageHooksLogProvider, ] }) -class SinglePageModule {} +class SinglePageModule { } @NgModule({ bootstrap: [ @@ -108,7 +108,7 @@ class SinglePageModule {} multiPageHooksLogProvider, ] }) -class MultiPageModule {} +class MultiPageModule { } @NgModule({ bootstrap: [ @@ -137,14 +137,14 @@ class MultiPageModule {} lazyLoadHooksLogProvider, ] }) -class LazyLoadModule {} +class LazyLoadModule { } application.start({ create: (): Page => { const page = new Page(); page.content = root; - let onLoadedHandler = function(args) { + let onLoadedHandler = function (args) { page.off('loaded', onLoadedHandler); //profiling.stop('application-start'); console.log('Page loaded');