FeatureModule fails during an AOT build when static forRoot has arguments
Asked Answered
D

1

10

I am encountering an AOT build issue using [email protected].

The error is:

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)
            ]
        }
    }
}

This compiles successfully in non-aot builds. This seems to only be a problem for AOT builds.

Why is this error happening?

Danilodanio answered 7/12, 2017 at 2:31 Comment(7)
This will not solve your problem, but might maybe bring you closer to solve it. Angular 5.1 was released today along with the CLI 1.6. One of the fixed issues is Improved decorator error messages. Maybe try to upgrade to the latest version and see if you can find a way to solve this. Sorry for not being to help more. blog.angular.io/angular-5-1-more-now-available-27d372f5eb4ePeepul
thank-you, I appreciate the advice. I'll try running CLI 1.6 to see if the errors are more informative.Danilodanio
The error still exists in the latest @angular/cli.Danilodanio
Have you tried to remove var routes - data and use providers: [provideRoutes(data)]?Expedient
In Feature module you wrote imports: [ RouterModule.forRoot() ], shouldn't it be forRoot([])? Can you setup reproduction?Expedient
Yes, you're right, sorry another typoDanilodanio
You need to provide a reproduction of this issue. Using what you said above, I tried and can't reproduce the issue. Here's what I did (you can download this as a CLI project, run npm install && ng build --prod and everything works fine. stackblitz.com/edit/angular-k4dewwPhaih
D
6

Ok, it took me a while to figure this out. TLDR: the forRoot method must dead simple, otherwise the AOT compiler complains.

To make it simple, I had to:

  1. Remove branching logic and function calls from the forRoot method.

  2. Implement the logic to map the items to routes into a factory provider, rather than inlining it inside the forRoot method.

  3. Use Router.resetConfig to add the routes dynamically within the factory impelmentation.

  4. Add an ANALYZE_FOR_ENTRY_COMPONENTS provider so that any components passed in would be added to entryComponents automatically as part of the module.

  5. Import RouterModule.forChild([]) into FeatureModule because I use the components from @angular/router.

  6. Import RouterModule.forRoot([]) into AppModule because it provides the application-wide Router service.

Final Solution

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 {
}

The key to solving this was the realization that RouterModule.forChild() does not register any router services. This is intentional so that any module can import the RouterModule and take advantage of its components, without actually registering any services. At the AppModule level, I still needed to register the Router service as a singleton by importing RouterModule.forRoot() into AppModule.

Danilodanio answered 8/12, 2017 at 8:17 Comment(1)
Can you share your MyService class?Iluminadailwain

© 2022 - 2024 — McMap. All rights reserved.