How to inject different service based on certain build environment in Angular2?

后端 未结 9 1611
醉话见心
醉话见心 2020-12-04 08:50

I have HeroMockService that will return mocked data and HeroService that will call back end service to retrieve heroes from database.

Assum

相关标签:
9条回答
  • 2020-12-04 09:18

    @Inject('IHeroService') does't go well with production. In my case I just did this:

    import { Injectable } from '@angular/core';
    import * as Priority from 'priority-web-sdk';
    
    
    @Injectable()
    export class PriorityService
    {
        priority;
        constructor( ){
            this.priority=Priority;
        }
    }
    

    The Priority library that I wanted to import did not have an exported module so I needed to cheat a little. At first I tried the @Inject('IHeroService') way but when I compiled as prod it didn't work.

    Hope it helps someone!

    0 讨论(0)
  • 2020-12-04 09:21

    This worked for me in Angular 6. Leverages @void's Interface approach (where both the true service and the mock service implement the service interface). The accepted answer neglects to mention that the purpose of a factory is to return an implementation of an interface.

    heroservice/heroservice.module.ts

    import { NgModule, ModuleWithProviders } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { IHeroService } from './ihero.service';
    import { HeroService } from './hero.service';
    import { MockHeroService } from './mock-hero.service';
    
    
    @NgModule({
      imports: [
        CommonModule
      ],
    })
    export class HeroServiceModule {
      static forRoot(mock: boolean): ModuleWithProviders {
        return {
          ngModule: HeroServiceModule ,
          providers: [
            {
              provide: IHeroService,
              useClass: mock ? MockHeroService : HeroService,
            },
          ],
        };
      }
    }
    

    app.module.ts

    import { NgModule, isDevMode } from '@angular/core';
    import { HeroServiceModule } from './heroservice/heroservice.module';
    
    // ...
    
      imports: [
        BrowserModule,
        HttpClientModule,
        // ...
        HeroServiceModule.forRoot(
          isDevMode() // use mock service in dev mode
        ),
      ],
    
    // ...
    

    some/some.component.ts

    Import and use the interface as you would the actual service (the implementation will be "provided" at runtime).

    import { IHeroService } from '../heroservice/iheroservice.service';
    
    // ...
    
    0 讨论(0)
  • 2020-12-04 09:22

    A lot of these answers are correct but will fail tree shaking. The mock services will be included as part of the final application which is usually not what is desired. In addition, it isn't easy to switch in and out of mock mode.

    I have created a working example which solves these problems: https://github.com/westonpace/angular-example-mock-services

    1. For each service, create an abstract class or a value token & interface. Create a mock service and a real service which implement the abstract class / interface.
    2. Create a MockModule which provides all of your mock services and a RealModule which provides all of your real services (make sure to use the useClass/provide fields to provide the interface/abstract class)
    3. In the appropriate environment.*.ts files load either the RealModule or the MockModule
    4. Make changes to the angular.json file used by angular-cli to create a new build target mock which builds with the mock environment file which injects the MockModule. Create a new serve configuration which serves up the mock build so you can do ng serve -c mock. Change the default protractor configuration so that it uses the mocked serve target so ng e2e will run against your mock services.
    0 讨论(0)
  • 2020-12-04 09:34

    Here's an example of the latest way to do it with conditional environment logic. Note that EventService and LogService are just examples of other services you may have).

    providers: [
        EventService,
        LogService,
        MyService,
        {
          provide: MyService,
          useClass: (environment.custom == null || environment.custom === '' || environment.custom === 'MyCustomerService')
            ? MyCustomService : MyService
        }
      ]
    ]
    
    0 讨论(0)
  • 2020-12-04 09:35

    See below my solution is based on @peeskillet one.

    Create an interface that both the mock and real service implement, for your example IHeroService.

    export interface IHeroService {
        getHeroes();
    }
    
    export class HeroService implements IHeroService {
        getHeroes() {
        //Implementation goes here
        }
    }
    
    export class HeroMockService implements IHeroService {
        getHeroes() {
        //Mock implementation goes here
    }
    

    Assuming you've created the Angular app using Angular-CLI, in your environment.ts file add the appropriate implementation, e.g.:

    import { HeroService } from '../app/hero.service';
    
    export const environment = {
      production: false,
      heroService: HeroService
    };
    

    For every different environment.(prod|whatever).ts you have to define a heroService pointing to the implementation and add the import.

    Now, say you want to import the service in the AppModule class (you can do it on the component where you need the service as well)

    @NgModule({
      declarations: [
        AppComponent,
      ],
      imports: [
        FormsModule,
        HttpModule,
        AlertModule.forRoot()
      ],
      providers: [
        {
          provide: 'IHeroService',
          useClass: environment.heroService
        }
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    The important part is the provider:

      providers: [
        {
          provide: 'IHeroService',
          useClass: environment.heroService
        }
    

    Now wherever you want to use the service you have to do the following:

    import { IHeroService } from './../hero.service';
    
    export class HeroComponent {
    
    constructor(@Inject('IHeroService') private heroService: IHeroService) {}
    

    Sources: Is it possible to inject interface with angular2? https://medium.com/beautiful-angular/angular-2-and-environment-variables-59c57ba643be http://tattoocoder.com/angular-cli-using-the-environment-option/

    0 讨论(0)
  • 2020-12-04 09:37

    For those like me getting the following error:

    ERROR in Error encountered resolving symbol values statically.
    Function calls are not supported. Consider replacing the function
    or lambda with a reference to an exported function
    

    For my purpose it was for an ErrorHandler:

    {
        provide: ErrorHandler,
        useFactory: getErrorHandler,
        deps: [ Injector ]
    }
    ...
    export function getErrorHandler(injector: Injector): ErrorHandler {
        if (environment.production) {
            return new MyErrorHandler(injector);
        } else {
            return new ErrorHandler();
        }
    }
    
    0 讨论(0)
提交回复
热议问题