当静态 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 编译器会报错。

为了简单起见,我必须:

  1. forRoot 方法中删除分支逻辑和函数调用。

  2. 实现将项目映射到工厂提供程序的路由的逻辑,而不是将其内联到 forRoot 方法中。

  3. 使用Router.resetConfig在工厂实现中动态添加路由。

  4. 添加一个 ANALYZE_FOR_ENTRY_COMPONENTS 提供程序,以便传入的任何组件都将作为模块的一部分自动添加到 entryComponents

  5. RouterModule.forChild([]) 导入 FeatureModule 因为我使用了 @angular/router 中的组件。

  6. 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 服务注册为单例。