Подтвердить что ты не робот

Можно ли вводить интерфейс с помощью angular2?

Интересно, есть ли способ внедрить интерфейсы в Angular2? (см. ниже)

Я думаю, что это связано с отсутствующим декоратором @Injectable() на интерфейсе, но, похоже, это не разрешено.

С уважением.

Когда CoursesServiceInterface реализуется как интерфейс, компилятор TypeScript жалуется: "CoursesServiceInterface не может найти имя":

import {CoursesServiceInterface} from './CoursesService.interface';
import {CoursesService} from './CoursesService.service';
import {CoursesServiceMock} from './CoursesServiceMock.service';
bootstrap(AppComponent, [
  ROUTER_PROVIDERS, 
  GlobalService,
  provide(CoursesServiceInterface, { useClass: CoursesServiceMock })
  ]);

но с CoursesServiceInterface в качестве интерфейса:

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; };
}

Когда сервис является классом, компилятор TypeScript больше не жалуется:

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; };
}
4b9b3361

Ответ 1

Нет, интерфейсы не поддерживаются для DI. С интерфейсами TypeScript больше не доступны во время выполнения, только статически и поэтому не могут использоваться в качестве токенов DI.

В качестве альтернативы вы можете использовать строки как клавиши или InjectionToken

provide('CoursesServiceInterface', {useClass: CoursesServiceMock}) // old

Забастовкa >

providers: [{provide: 'CoursesServiceInterface', useClass: CoursesServiceMock}]

и введите его как

constructor(@Inject('CoursesServiceInterface') private coursesService:CoursesServiceInterface) {}

См. также https://angular.io/api/core/InjectionToken

Ответ 2

Причина, по которой вы не можете использовать интерфейсы, заключается в том, что интерфейс является артефактом времени разработки TypeScript. JavaScript не имеет интерфейсов. Интерфейс TypeScript исчезает из сгенерированного JavaScript. Информация о интерфейсе отсутствует для Angular для поиска во время выполнения.


Решение 1:

Самое простое решение - просто определить абстрактный класс, реализующий интерфейс. Часто вам нужен абстрактный класс.

Интерфейс:

import {Role} from "../../model/role";

export interface ProcessEngine {

     login(username: string, password: string):string;

     getRoles(): Role[];
}

Абстрактный класс:

import {ProcessEngine} from "./process-engine.interface";

export abstract class ProcessEngineService implements ProcessEngine {

    abstract login(username: string, password: string): string;

    abstract getRoles(): Role[];

}

Класс бетона:

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[] {...}

}

Теперь вы можете определить своего провайдера, как обычно:

@NgModule({
      ...
      providers: [
        ...,
        {provide: ProcessEngineService, useClass: WebRatioEngineService}
      ]
})

Решение 2:

В официальной документации Angular предлагается использовать InjectionToken, аналогично OpaqueToken. Вот пример:

Ваш интерфейс и класс:

export interface AppConfig {
   apiEndpoint: string;
   title: string;
}

export const HERO_DI_CONFIG: AppConfig = {
  apiEndpoint: 'api.heroes.com',
  title: 'Dependency Injection'
};

Определите свой токен:

import { InjectionToken } from '@angular/core';

export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');

Зарегистрируйте поставщика зависимостей, используя объект InjectionToken, например, в вашем приложении .module.ts:

providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]

Чем вы можете вставить объект конфигурации в любой конструктор, который ему нужен, с помощью декоратора @Inject:

constructor(@Inject(APP_CONFIG) config: AppConfig) {
     this.title = config.title;
}

Ответ 3

Используйте OpaqueToken, интерфейсы не поддерживаются DI, потому что у Javascript нет интерфейсов. Один из способов сделать это в Angular 2 - с помощью OpaqueToken. https://angular.io/docs/ts/latest/guide/dependency-injection.html

import { OpaqueToken } from '@angular/core';

export let APP_CONFIG = new OpaqueToken('app.config');

providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]

constructor(@Inject(APP_CONFIG) config: AppConfig) {
  this.title = config.title;
}

Надеюсь, это поможет.

Ответ 4

Мой опыт (исходящий из разработки java-backend) в frontend dev выглядит следующим образом.

Если мы говорим об "интерфейсе", у меня есть сильное ожидание того, что основной принцип использования интерфейса обеспечивается языками, которые "предлагают" интерфейс. Который: "код против интерфейса не против реализации".

Это не гарантируется typescript/angular2. (чем они не должны использовать интерфейс слова, возможно).

Что было в моем случае (предупреждение: я изучаю angular2, поэтому мой обходной способ может показаться уродливым для продвинутых пользователей):
Компонент A1 имеет дочерний компонент B.
Компонент B должен знать родителя и вызвать метод для родителя.
Таким образом, компонент B получает родителя через DependencyInjection в нем конструктор.

   constructor( private a: A1Component) {}

Все в порядке.
Чем все усложняется.
Другой компонент A2 может быть родителем comp. B.
В идеале я должен ввести в B интерфейс (а не реализацию), который реализуется как A1, так и A2 (это было бы естественно в java-мире).
Чем B будет работать с этим интерфейсом. Если необходимо, то приведение типа к A2, например, сделает B осведомленным, если экземпляр, который у него есть, действительно A2 или нет.

Я говорю о простых компонентах/классах, а не об услугах (я вижу, что большинство решений относится к службам).
Я попытался использовать @Host(), @Injectable(), OpaqueToken, Providers, но всегда была ошибка. Когда, в конце концов, это сработало: на самом деле объект, введенный в Компонент B, был пустым объектом, а не родителем - возможно, я ошибочно использовался для провайдеров, а новый пустой объект был создан вместо того, чтобы вводить родительский объект.

Что я сделал в конце.. Я не использовал интерфейс. Я создал простой базовый класс для A1 и A2 - позвонил ему ABase.
Компонент B сохранит ссылку на этот базовый класс. Ссылка будет установлена ​​в конструкторе следующим образом:

//BComponent:
 parent: ABase;    

 constructor(@Optional parentA1: A1Component, @Optional parentA2: A2Component) {
    if( parentA1 )
       this.parent = parentA1;
    else
       this.parent = parentA2
 }

Да, это странное обходное решение, а не приятное (исходящее из Java-мышления, я согласен) - но у меня просто закончилось время и разочаровался в "интерфейсе".