Tuesday, November 1, 2016

Migrating to Ionic 2 RC 0 - ngc fails

Finally, I had my app migrated to Ionic 2 RC 0. I tested the app with ionic serve, I built and deployed the app with cordova run - everything was fine.
However, since the beginning of the process was ionic serve, this meant that from an Ionic point of view, everything happened in development mode. So I had no benefit from the performance gain by using Angular's AoT (Ahead-of-time compilation).

So I tried to do a real production build with ionic build. Unfortunately, it produced an error:
[12:51:11]  ionic-app-scripts 0.0.39
[12:51:11]  build prod started ... 
[12:51:11]  clean started ... 
[12:51:11]  clean finished in 7 ms 
[12:51:11]  copy started ... 
[12:51:11]  ngc started ... 
[12:51:11]  copy finished in 455 ms 
[12:51:12]  lint started ... 
[12:51:22]  lint finished in 9.64 s 
[12:51:32]  Error: Error at .../.tmp/app/app.module.ngfactory.ts:415:84 
[12:51:32]  Supplied parameters do not match any signature of call target. 
[12:51:32]  ngc failed 
[12:51:32]  ionic-app-script task: "build" 
[12:51:32]  Error: Error 
I had no idea, what was going wrong... Then I checked line 415 in app.module.ngFactory.ts (a generated file in the .tmp folder:
get _LoggingService_76():import46.LoggingService {
  if ((this.__LoggingService_76 == (null as any))) { (this.__LoggingService_76 = new import46.LoggingService()); }
    return this.__LoggingService_76;
}
Ok, it has something to do with my LoggingService, which I am using as provider in app.module.ts:
@NgModule({
  ...
  providers: [
    ConfigurationService
    LoggingService,
    ...
  ]
})
Then I checked the declaration of the LoggingService:
export declare class LoggingService {
  private configurationService;
  constructor(configurationService: ConfigurationService);
  ...
}
As you can see, its constructor has a parameter, but the code in app.module.ngFactory.ts is using a parameterless constructor.

One workaround could be to changed the parameter to an optional one. A better workaround is to change the declaration of the provider in app.module.ts:
@NgModule({
  ...
  providers: [
    ConfigurationService,
    {
      provide: LoggingService,
      useFactory: getLoggingService,
      deps: [ConfigurationService]
    },  
    ...
  ]
})
The last piece is the factory function:
export function getLoggingService(configurationService: ConfigurationService): LoggingService {
  return new LoggingService(configurationService);
}
That's it. Still a workaround, but working.