i wonder if there is a proper way to inject interfaces in Angular2? (cf. below)
I think this is related with the missing @Injectable() decorator on the interface, but it seems this is not allowed.
Regards.
When CoursesServiceInterface is implemented as an interface, the TypeScript compiler complains "CoursesServiceInterface cannot find name":
import {CoursesServiceInterface} from './CoursesService.interface'; import {CoursesService} from './CoursesService.service'; import {CoursesServiceMock} from './CoursesServiceMock.service'; bootstrap(AppComponent, [ ROUTER_PROVIDERS, GlobalService, provide(CoursesServiceInterface, { useClass: CoursesServiceMock }) ]);
but with CoursesServiceInterface as an interface:
import {Injectable} from 'angular2/core'; import {Course} from './Course.class'; //@Injectable() export interface CoursesServiceInterface { getAllCourses(): Promise<Course[]>;//{ return null; }; getCourse(id: number): Promise<Course>;// { return null; }; remove(id: number): Promise<{}>;// { return null; }; }
When service is a class, the TypeScript compiler doesn't complain anymore:
import {Injectable} from 'angular2/core'; import {Course} from './Course.class'; @Injectable() export class CoursesServiceInterface { getAllCourses() : Promise<Course[]> { return null; }; getCourse(id: number) :Promise<Course> { return null; }; remove (id: number) : Promise<{}> { return null; }; }
There are basically three types of dependency injection: interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.
Interface Injection is similar to Getter and Setter DI, the Getter, and Setter DI use default getter and setter but Interface Injection uses support interface a kind of explicit getter and setter which sets the interface property.
The @Injectable() decorator defines a class as a service in Angular and allows Angular to inject it into a component as a dependency. Likewise, the @Injectable() decorator indicates that a component, class, pipe, or NgModule has a dependency on a service. The injector is the main mechanism.
We use the @Inject parameter decorator to instruct Angular we want to resolve a token and inject a dependency into a constructor. We use the @Injectable class decorators to automatically resolve and inject all the parameters of class constructor.
No, interfaces are not supported for DI. With TypeScript interfaces are not available at runtime anymore, only statically and therefore can't be used as DI tokens.
Alternatively you can use strings as keys or InjectionToken
provide('CoursesServiceInterface', {useClass: CoursesServiceMock}) // old
providers: [{provide: 'CoursesServiceInterface', useClass: CoursesServiceMock}]
and inject it like
constructor(@Inject('CoursesServiceInterface') private coursesService:CoursesServiceInterface) {}
See also https://angular.io/api/core/InjectionToken
The reason you can't use interfaces is because an interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces. The TypeScript interface disappears from the generated JavaScript. There is no interface type information left for Angular to find at runtime.
Solution 1:
The easiest solution is just to define an abstract class which implements the interface. Often, you need a abstract class anyway.
Interface:
import {Role} from "../../model/role"; export interface ProcessEngine { login(username: string, password: string):string; getRoles(): Role[]; }
Abstract Class:
import {ProcessEngine} from "./process-engine.interface"; export abstract class ProcessEngineService implements ProcessEngine { abstract login(username: string, password: string): string; abstract getRoles(): Role[]; }
Concrete Class:
import { Injectable } from '@angular/core'; import {ProcessEngineService} from "./process-engine.service"; @Injectable() export class WebRatioEngineService extends ProcessEngineService { login(username: string, password: string) : string {...} getRoles(): Role[] {...} }
Now you can define your provider like usual:
@NgModule({ ... providers: [ ..., {provide: ProcessEngineService, useClass: WebRatioEngineService} ] })
Solution 2:
The official documentation of Angular suggest to use the InjectionToken, similar to OpaqueToken. Here is the Example:
Your interface and class:
export interface AppConfig { apiEndpoint: string; title: string; } export const HERO_DI_CONFIG: AppConfig = { apiEndpoint: 'api.heroes.com', title: 'Dependency Injection' };
Define your Token:
import { InjectionToken } from '@angular/core'; export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');
Register the dependency provider using the InjectionToken object, e.g in your app.module.ts:
providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
Than you can inject the configuration object into any constructor that needs it, with the help of an @Inject decorator:
constructor(@Inject(APP_CONFIG) config: AppConfig) { this.title = config.title; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With