import {NgModuleFactoryLoader} from '@angular/core';
import {AsyncNgModuleLoader} from './async-ng-module-loader';
// Add to main providers
{provide: NgModuleFactoryLoader, useClass: AsyncNgModuleLoader}
-
-
Save brandonroberts/02cc07face25886fe142c4dbd8da1340 to your computer and use it in GitHub Desktop.
import {Injectable, NgModuleFactory, NgModuleFactoryLoader, Compiler, Type} from '@angular/core'; | |
class LoaderCallback { | |
constructor(public callback) {} | |
} | |
export let load: Type = (callback: Function) => { | |
return new LoaderCallback(callback); | |
}; | |
/** | |
* NgModuleFactoryLoader that uses Promise to load NgModule type and then compiles them. | |
* @experimental | |
*/ | |
@Injectable() | |
export class AsyncNgModuleLoader implements NgModuleFactoryLoader { | |
constructor(private compiler: Compiler) {} | |
load(modulePath: string|LoaderCallback): Promise<NgModuleFactory<any>> { | |
if (modulePath instanceof LoaderCallback) { | |
let loader = (modulePath as LoaderCallback).callback(); | |
return Promise | |
.resolve(loader) | |
.then((type: any) => checkNotEmpty(type, '', '')) | |
.then((type: any) => this.compiler.compileModuleAsync(type)); | |
} | |
return Promise.resolve(null); | |
} | |
} | |
function checkNotEmpty(value: any, modulePath: string, exportName: string): any { | |
if (!value) { | |
throw new Error(`Cannot find '${exportName}' in '${modulePath}'`); | |
} | |
return value; | |
} |
I get Error: path.split is not a function
Did you register provider?
{provide: NgModuleFactoryLoader, useClass: AsyncNgModuleLoader}
@iurii-kyrlenko nice! Even you want something less verbose, look into the es6-promise-loader: https://www.npmjs.com/package/es6-promise-loader. It lets you provide a path and namespace also.
Thank you, @brandonroberts!
I cannot pass namespace (export name) as a function parameter when defining routes:
{ path: 'task2', loadChildren: require('es6-promise!./task-2/task2.module')('Task2Module') }
because it's called immediately and breaks the 'laziness'. So, I still have to use a helper load
:
{ path: 'task2', loadChildren: load(require('es6-promise!./task-2/task2.module'), 'Task2Module') }
which calls the function in NgModuleFactoryLoader#load
.
Ah Thanks @iurii-kyrylenko - I actually didn't register the provider. Now it works after i registered it in the main app.module.ts file.
You are the man !! :)
Awesome gist!
@iurii-kyrylenko that is correct. You still need to wrap it in the load
function for now. Once RC6 lands, you'll be able to use the require
function directly
Works Perfect! Awesome work!
I notice: in my project there two modules with children module integration. Webpack build without errors but I find two new *.js files in root folder: 1.js and 4.js Should I import this files into project befor publish or this files imported into main.js......?
I have some difficulties with location were I should register provider. So for others who try implemet this solution...in app.module.ts
:
providers: [
{ provide: NgModuleFactoryLoader, useClass: AsyncNgModuleLoader },
...
]
i the routermodule
DarienF, put it in the router module.
EXCEPTION: Error: Uncaught (in promise): TypeError: loadChildren is not a function
I got error like this... ^
do I miss something?
at app.routing.ts
const routes: Routes = [
{
path : 'dev',
loadChildren: load(() => new Promise(resolve => {
return (require as any).ensure([], (require: any) => {
return resolve(require('./pages/dev/dev.module').default);
});
}))
},
{
path : '',
component: HomeComponent
}
];
at app.module.ts
providers : [
{provide: NgModuleFactoryLoader, useClass: AsyncNgModuleLoader},
AuthGuard,
SessionService,
],
Hello @brandonroberts,
I'm having same issue as @lialosiu, can you please provide some advise?
Thank you,
Laurentiu
loadChildren: () => new Promise(resolve => {
return (require as any).ensure([], (require: any) => {
return resolve(require('./pages/dev/dev.module').default);
});
})
did the trick for me. Notice the absence of load()
@forsak3n yes, that will work once RC6 lands and you won't need the loader anymore at all 😄
it's work, awesome gist. more thanks for you. 👍
I get a "Generic type 'Type' requires 1 type argument(s)." after upgrading to rc6. Replacing Type
by Type<any>
won't fix it.
Just tried @forsak3n s' solution, my routes is like
export const loginRoutes: Routes = [
{
path: 'login',
loadChildren: () => new Promise(resolve => {
return (require as any).ensure([], (require: any) => {
return resolve(require('./login.module/login.module').LoginModule);
});
})
}];
export const loginRouting = RouterModule.forChild(loginRoutes);
then, I get "Type '() => Promise<{}>' is not assignable to type 'string'."
The following works for my startup in RC6:
{ path: 'task2', loadChildren: () =>
new Promise(resolve =>
(require as any).ensure([], () =>
resolve(require('./task-2/task2.module').Task2Module)
)
)
}
@AnotherStop, have you updated your typings.json
?
Hi @iurii-kyrylenko,
Can you please help me a bit as I'm out of solutions. I have tried your solution but I'm getting "No NgModule metadata found for '[object Promise]'" error. I want to mention that I don't use Typescript. I have placed bellow the code I'm using:
- router
{
path: 'style-guide',
loadChildren: () =>
new Promise(resolve => {
return require.ensure([], () =>
resolve(require('./style-guide.module').StyleGuideModule)
);
}
-style guide module
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import StyleGuideComponent from './style-guide.component';
@NgModule({
imports: [
CommonModule,
RouterModule.forChild([
{ path: '', component: StyleGuideComponent }
])
],
declarations: [
StyleGuideComponent
]
})
export class StyleGuideModule {}
Thank you,
Laurentiu
@iurii-kyrylenko I am transpiling my code with babel and I m using a decorator plugin. That s why I can use decorators without Typescript and I don t have a tsconfig file.
Thank you
Laurențiu
the same issue with @lialosiu
@qk44077907 @lialosiu are you still having this issue? With RC6 you don't need a custom loader anymore. I also have a webpack loader here you can use if you want to use string-based lazy loading: https://www.npmjs.com/package/angular2-router-loader
hello can anyone help me,
Work this issue with RC5.
{
path: 'admin',
loadChildren: load(() => new Promise(resolve => {
return (require as any).ensure([], (require: any) => {
return resolve(require('./pages/dev/dev.module').default);
});
}))
},
because of require cannot find name.
export let load: Type = (callback: Function) => {
return new LoaderCallback(callback);
};
Generic type 'Type' requires 1 type argument(s)
Getting above error while compile with ng serve
node -v 7.0.0
npm -v 3.10.8
angular-cli: 1.0.0-beta.19-3
editor : Visual Studio Code
please help me...
"dependencies": {
"@angular/common": "~2.1.0",
"@angular/compiler": "~2.1.0",
"@angular/core": "~2.1.0",
"@angular/forms": "~2.1.0",
"@angular/http": "~2.1.0",
"@angular/platform-browser": "~2.1.0",
"@angular/platform-browser-dynamic": "~2.1.0",
"@angular/router": "~3.1.0",
"bootstrap": "^3.3.7",
"core-js": "^2.4.1",
"jquery": "^3.1.1",
"rxjs": "5.0.0-beta.12",
"ts-helpers": "^1.1.1",
"zone.js": "^0.6.23"
},
Same error than @masagatech trying to fix it right now.
same error @masagatech trying to fix it right now
@brandonroberts, thank you for the gist!
I slightly modified/refactored your solution in my startup template.
To the
load
function I added the second optional parameterexportName
. WhenexportName
is missed - it's implied that module has the default export.This is a my routing and provider registration