我正在开发一个与webpack捆绑在一起的Angular2应用程序。一切都很顺利。然而,这个捆绑好的应用程序将被独立地运送,并且在运行时,在引导应用程序之前,我需要将其他NgModules指定到我打包在应用程序中的模块中。基本上,可以将其视为包含在index.html中的两个包,当引导之后,包1进入包2。只有包2引导了一个应用程序。因此,我进行了一些实验,想与您分享一下(如果我采取了一种相当肮脏和完全错误的方法,请纠正我。尽管如此,如果这是唯一的方法,我不介意肮脏。)唯一的要求是在转译期间不应进行任何操作。要求是我可以在我的index.html中包括一个或多个包,并且它们可以在没有彼此先前了解的情况下连接到实际应用程序中。
我之所以首先要这样做,是因为我在一个平台上工作,其他开发人员可以通过安装扩展来挂接代码。这意味着我提供最初的包,它在编译期间完全不知道其他包的存在。在AngularJS中很容易实现,但是转向Angular2及以后版本后,这变得不那么容易了。
我使用以下方法成功延迟了应用程序的引导:
在main.ts中:
window["platformBrowserDynamicRef"] = platformBrowserDynamic();
window["appModule"] = AppModule;
这个工作得很好,当我手动调用bootstrap时,应用程序能够正常启动。 我创建了另一个bundle,作为另一个独立的应用程序也可以正常运行。
但是,当我尝试将它们通过像上面那样隐式地导入组件并引导应用程序时,会出现一个我不知道如何解决的错误。
window["app"].modulesImport.push(CommonModule);
window["app"].modulesImport.push(FormsModule);
window["app"].modulesImport.push(BrowserModule);
window["app"].modulesImport.push(NgGridModule);
window["app"].modulesImport.push(ReactiveFormsModule);
window["app"].modulesImport.push(BrowserAnimationsModule);
window["app"].modulesImport.push(HttpModule);
@NgModule({
imports: window["app"].modulesImport,
declarations: [
DYNAMIC_DIRECTIVES,
PropertyFilterPipe,
PropertyDataTypeFilterPipe,
LanguageFilterPipe,
PropertyNameBlackListPipe
],
exports: [
DYNAMIC_DIRECTIVES,
CommonModule,
FormsModule,
HttpModule
]
})
export class PartsModule {
static forRoot()
{
return {
ngModule: PartsModule,
providers: [ ], // not used here, but if singleton needed
};
}
}
将模块放在隐式数组中运行良好,但是一旦我从另一个包中添加一个模块,就会出现以下错误:
Uncaught Error: Unexpected value 'ExtensionsModule' imported by the module 'PartsModule'. Please add a @NgModule annotation.
at syntaxError (http://localhost:3002/dist/app.bundle.js:43864:34) [<root>]
at http://localhost:3002/dist/app.bundle.js:56319:44 [<root>]
at Array.forEach (native) [<root>]
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
at CompileMetadataResolver.getNgModuleSummary (http://localhost:3002/dist/app.bundle.js:56244:52) [<root>]
at http://localhost:3002/dist/app.bundle.js:56317:72 [<root>]
at Array.forEach (native) [<root>]
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
at CompileMetadataResolver.getNgModuleSummary (http://localhost:3002/dist/app.bundle.js:56244:52) [<root>]
at http://localhost:3002/dist/app.bundle.js:56317:72 [<root>]
at Array.forEach (native) [<root>]
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
at JitCompiler._loadModules (http://localhost:3002/dist/app.bundle.js:67404:64) [<root>]
at JitCompiler._compileModuleAndComponents (http://localhost:3002/dist/app.bundle.js:67363:52) [<root>]
我还尝试了对ExtensionsModule进行AoT编译,但也没有成功。同时,我尝试使用JitCompiler在运行时动态编译组件,以查看是否可以通过这种方式将它们引入应用程序。不过,出现了相同的错误。
组件是在应用程序引导期间还是在转译期间被装饰的?查看隐式数组中的模块,我可以看到它们都被装饰了,但我的模块没有。这是Webpack插入了装饰器方法的调用代码吗?我认为在转译期间装饰器信息会丢失,所以也许我需要暂时存储它,以便在其他地方获取它。(注意:作为第一个元素的extensionsmodule不包含装饰器数组)
我想我正在寻找如何从这里继续前进的指针。或许NgModule注释函数是一个不错的起点。不过我无法在任何地方找到它。
编辑
深入研究了这个问题后,我尝试了使用路由器进行延迟加载的方法,但是在尝试使用js文件加载时出现了这个错误(网络选项卡显示它从未发出文件请求):
const appRoutes: Routes = [
{
path: 'edit-entity/:type/:id/:culture',
component: EntityEditor,
resolve: {
entity: EntityResolver,
navigation: NavigationResolver
},
data: { shouldDetach: true},
loadChildren: "/dist/extensions.bundle.js#ExtensionsBundle",
},
{
path: '',
component: Dashboard,
resolve: {
navigation: NavigationResolver
},
data: { shouldDetach: true}
}
];
以下错误解决方案:
"Could not find module with name ExtensionsModule.js"
其次,我尝试使用如下的 loadChildren 函数:
const appRoutes: Routes = [
{
path: 'edit-entity/:type/:id/:culture',
component: EntityEditor,
resolve: {
entity: EntityResolver,
navigation: NavigationResolver
},
data: { shouldDetach: true},
loadChildren: () => {
//"/dist/extensions.bundle.js#ExtensionsModule",
// return System.import('./dynamic.module').then((comp: any) => {
// return comp.otherExport;
// });
return window["lux"].modulesLazyImport[0];
}
},
{
path: '',
component: Dashboard,
resolve: {
navigation: NavigationResolver
},
data: { shouldDetach: true}
}
];
解决时间:
app.bundle.js:1548 ERROR Error: Uncaught (in promise): Error: No NgModule metadata found for 'ExtensionsModule'.
Error: No NgModule metadata found for 'ExtensionsModule'.
at NgModuleResolver.resolve (app.bundle.js:55709) [angular]
at CompileMetadataResolver.getNgModuleMetadata (app.bundle.js:56288) [angular]
at JitCompiler._loadModules (app.bundle.js:67404) [angular]
at JitCompiler._compileModuleAndComponents (app.bundle.js:67363) [angular]
at JitCompiler.compileModuleAsync (app.bundle.js:67325) [angular]
at ModuleBoundCompiler.compileModuleAsync (app.bundle.js:67693) [angular]
at MergeMapSubscriber.project (app.bundle.js:30608) [angular]
at MergeMapSubscriber._tryNext (app.bundle.js:69744) [angular]
at MergeMapSubscriber._next (app.bundle.js:69734) [angular]
at MergeMapSubscriber.Subscriber.next (app.bundle.js:14584) [angular]
at ScalarObservable._subscribe (app.bundle.js:69206) [angular]
at ScalarObservable.Observable.subscribe (app.bundle.js:118) [angular]
at MergeMapOperator.call (app.bundle.js:69709) [angular]
at Observable.subscribe (app.bundle.js:115) [angular]
at NgModuleResolver.resolve (app.bundle.js:55709) [angular]
at CompileMetadataResolver.getNgModuleMetadata (app.bundle.js:56288) [angular]
at JitCompiler._loadModules (app.bundle.js:67404) [angular]
at JitCompiler._compileModuleAndComponents (app.bundle.js:67363) [angular]
at JitCompiler.compileModuleAsync (app.bundle.js:67325) [angular]
at ModuleBoundCompiler.compileModuleAsync (app.bundle.js:67693) [angular]
at MergeMapSubscriber.project (app.bundle.js:30608) [angular]
at MergeMapSubscriber._tryNext (app.bundle.js:69744) [angular]
at MergeMapSubscriber._next (app.bundle.js:69734) [angular]
at MergeMapSubscriber.Subscriber.next (app.bundle.js:14584) [angular]
at ScalarObservable._subscribe (app.bundle.js:69206) [angular]
at ScalarObservable.Observable.subscribe (app.bundle.js:118) [angular]
at MergeMapOperator.call (app.bundle.js:69709) [angular]
at Observable.subscribe (app.bundle.js:115) [angular]
at resolvePromise (app.bundle.js:77503) [angular]
at resolvePromise (app.bundle.js:77488) [angular]
at :3000/dist/app.bundle.js:77537:17 [angular]
at Object.onInvokeTask (app.bundle.js:4599) [angular]
at ZoneDelegate.invokeTask (app.bundle.js:77289) [angular]
at Zone.runTask (app.bundle.js:77179) [<root> => angular]
at drainMicroTaskQueue (app.bundle.js:77433) [<root>]
at HTMLDivElement.ZoneTask.invoke (app.bundle.js:77364) [<root>]
编辑 我认为错误可能是因为我的注释从未被调用。不确定是否是这种情况。我决定在这上面使用ES5,并像下面一样将组件推送到我的隐式列表中。尝试将组件推送到parts.module.ts声明部分时,仍然会产生相同的错误。
var HelloWorldComponent = function () {
};
HelloWorldComponent.annotations = [
new ng.core.Component({
selector: 'hello-world',
template: '<h1>Hello World!</h1>',
})
];
window["lux"].componentsLazyImport.push(HelloWorldComponent);
那么这是Angular 4.0.0中的一个bug还是根本不可能创建插件机制呢?
最好的问候 Morten