当静态 forRoot 有参数时,FeatureModule 在 AOT 构建期间失败
FeatureModule fails during an AOT build when static forRoot has arguments
我在使用 Angular@5.1.0 时遇到 AOT 构建问题。
错误是:
ERROR in Error during template compile of 'AppModule'
Function calls are not supported in decorators but 'FeatureModule' was called.
feature.module.ts
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot([])
],
declarations: [
...
],
exports: [
...
]
})
export class FeatureModule{
static forRoot(items:any[]): ModuleWithProviders {
const routes:Routes = items.map(t=> {
return { path: t.name, component: t.component };
});
return {
ngModule: FeatureModule,
providers: [
provideRoutes(routes)
]
}
}
}
这在非 aot 构建中编译成功。这似乎只是 AOT 构建的问题。
为什么会出现这个错误?
好的,我花了一段时间才弄明白。
TLDR:forRoot
方法必须非常简单,否则 AOT 编译器会报错。
为了简单起见,我必须:
从 forRoot
方法中删除分支逻辑和函数调用。
实现将项目映射到工厂提供程序的路由的逻辑,而不是将其内联到 forRoot
方法中。
使用Router.resetConfig
在工厂实现中动态添加路由。
添加一个 ANALYZE_FOR_ENTRY_COMPONENTS 提供程序,以便传入的任何组件都将作为模块的一部分自动添加到 entryComponents
。
将 RouterModule.forChild([])
导入 FeatureModule
因为我使用了 @angular/router
中的组件。
将 RouterModule.forRoot([])
导入 AppModule
因为它提供应用程序范围的 Router
服务。
最终解决方案
export const Items = new InjectionToken<any[]>('items');
export function InitMyService(router:Router, items:any[]) {
var routes:Routes = items.map(t=> { return { path: t.name, component: t.component, outlet: 'modal' }});
var r = router.config.concat(routes);
router.resetConfig(r);
return new MyService(router);
}
@NgModule({
imports: [
CommonModule,
RouterModule.forChild([])
],
declarations: [
MyComponent
],
exports: [
MyComponent
],
providers: [
]
})
export class FeatureModule {
static forRoot(items:any[]): ModuleWithProviders {
return {
ngModule: FeatureModule,
providers: [
{ provide: Items, useValue: items},
{ provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: items},
{ provide: MyService, useFactory: InitMyService, deps:[Router, Items] }
]
}
}
}
app.module.ts
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot([]),
FeatureModule.forRoot([{name: 'test', component: TestComponent}])
],
declarations: [ AppComponent, TestComponent ],
bootstrap: [ AppComponent ],
providers: [
],
exports: [AppComponent]
})
export class AppModule {
}
解决这个问题的关键是认识到 RouterModule.forChild()
没有注册任何路由器服务。这是故意的,以便任何模块都可以导入 RouterModule 并利用其组件,而无需实际注册任何服务。在 AppModule 级别,我仍然需要通过将 RouterModule.forRoot()
导入 AppModule 来将 Router
服务注册为单例。
我在使用 Angular@5.1.0 时遇到 AOT 构建问题。
错误是:
ERROR in Error during template compile of 'AppModule'
Function calls are not supported in decorators but 'FeatureModule' was called.
feature.module.ts
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot([])
],
declarations: [
...
],
exports: [
...
]
})
export class FeatureModule{
static forRoot(items:any[]): ModuleWithProviders {
const routes:Routes = items.map(t=> {
return { path: t.name, component: t.component };
});
return {
ngModule: FeatureModule,
providers: [
provideRoutes(routes)
]
}
}
}
这在非 aot 构建中编译成功。这似乎只是 AOT 构建的问题。
为什么会出现这个错误?
好的,我花了一段时间才弄明白。
TLDR:forRoot
方法必须非常简单,否则 AOT 编译器会报错。
为了简单起见,我必须:
从
forRoot
方法中删除分支逻辑和函数调用。实现将项目映射到工厂提供程序的路由的逻辑,而不是将其内联到
forRoot
方法中。使用
Router.resetConfig
在工厂实现中动态添加路由。添加一个 ANALYZE_FOR_ENTRY_COMPONENTS 提供程序,以便传入的任何组件都将作为模块的一部分自动添加到
entryComponents
。将
RouterModule.forChild([])
导入FeatureModule
因为我使用了@angular/router
中的组件。将
RouterModule.forRoot([])
导入AppModule
因为它提供应用程序范围的Router
服务。
最终解决方案
export const Items = new InjectionToken<any[]>('items');
export function InitMyService(router:Router, items:any[]) {
var routes:Routes = items.map(t=> { return { path: t.name, component: t.component, outlet: 'modal' }});
var r = router.config.concat(routes);
router.resetConfig(r);
return new MyService(router);
}
@NgModule({
imports: [
CommonModule,
RouterModule.forChild([])
],
declarations: [
MyComponent
],
exports: [
MyComponent
],
providers: [
]
})
export class FeatureModule {
static forRoot(items:any[]): ModuleWithProviders {
return {
ngModule: FeatureModule,
providers: [
{ provide: Items, useValue: items},
{ provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: items},
{ provide: MyService, useFactory: InitMyService, deps:[Router, Items] }
]
}
}
}
app.module.ts
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot([]),
FeatureModule.forRoot([{name: 'test', component: TestComponent}])
],
declarations: [ AppComponent, TestComponent ],
bootstrap: [ AppComponent ],
providers: [
],
exports: [AppComponent]
})
export class AppModule {
}
解决这个问题的关键是认识到 RouterModule.forChild()
没有注册任何路由器服务。这是故意的,以便任何模块都可以导入 RouterModule 并利用其组件,而无需实际注册任何服务。在 AppModule 级别,我仍然需要通过将 RouterModule.forRoot()
导入 AppModule 来将 Router
服务注册为单例。