Search code examples
angularionic-frameworkbuildtree-shaking

Is there a way in Angular 7 to decide at compile time which service to use?


I've created an Angular 7 project with Ionic 4. In this project I have multiple environments e.g 'local' or 'firebase'.

In my environment.ts is a parameter which corresponds to the strings above.

export const environment = {
  production: false,
  ...,
  userApi: 'firebase'
};

I've created a module to allow my services to be lazy-loaded as described in this article. It looks as follows:

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    AngularFireAuthModule,
    AngularFireModule.initializeApp(environment.firebase),
  ]
})
export class UserApiModule { }

Next I have an InjectionToken in which I declare that I provide the service in the Module mentioned above and use a factory to decide which Service to use.

export const USER_SERVICE = new InjectionToken<AbstractUserService>('USER_SERVICE',
    {
        providedIn: UserApiModule,
        factory: UserApiFactory
    }
);

function UserApiFactory(): AbstractUserService {
    switch (environment.userApi) {
        case 'firebase':
            return new FirebaseUserService(inject(AngularFireAuth));
        case 'mock':
            return new MockUserService();
        default:
            throw new Error('UserService implementation not found');
    }
}

This solutions in general works fine. When in 'firebase'-mode the firebase services is loaded and in 'local'-mode the local service.

But when compiling and serving in 'prod'-mode the package sizes in the ChromeDev-Tools are the same in either service mode. Which leads me to the idea that, also never reachable, the other service is still compiled.

So is there a way to implement this feature to ignore the effectively unreachable code? So if I set 'local' in my environment file, the firebase service aswell as the dependencies are never included in the build?

Thanks in advance!


Solution

    1. You can make use of conditional imports based on your environment configuration. There are multiple ways to do it but a easy one can be:
    @NgModule({
      imports: [ 
           BrowserModule,
           environment.userApi == 'firebase' ? AngularFireAuthModule : []
      ],
      declarations: [ AppComponent ],
      bootstrap:    [ AppComponent ]
    })
    export class AppModule {}
    
    1. You can also create an array of dependencies (imports) in your environment file and add them in imports like (see more here:
    imports: [...environment.dependencies]
    

    where

    //environment.ts
    dependencies = [
        AngularFireAuthModule,
        AngularFireModule.initializeApp(environment.firebase)
    ];