Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to inject interface with angular2?

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; }; } 
like image 630
user1568220 Avatar asked May 03 '16 11:05

user1568220


People also ask

Are interfaces dependency injection?

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.

What is Interface injection?

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.

What can be injected in Angular?

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.

What is difference between @inject and injectable?

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.


2 Answers

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

like image 139
Günter Zöchbauer Avatar answered Sep 21 '22 03:09

Günter Zöchbauer


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; } 
like image 28
Tobi Avatar answered Sep 22 '22 03:09

Tobi