Впрыск зависимости в абстрактный класс typescript (Angular2)

У меня есть абстрактный класс (без конструктора), и я хочу ввести в него еще один класс.

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

import { ErrorHandler } from '../../shared/services/errorHandler.service';
import { Inject } from '@angular/core';


export abstract class BaseApiComponent<T> {
    @Inject(ErrorHandler) errorHandler: ErrorHandler;

    this.errorHandler.test();
}

Введенный класс:

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

@Injectable()
export class ErrorHandler  {
  constructor() { }


  public test() {
    console.log('Test');
  }

}

И у меня есть следующая ошибка

ORIGINAL EXCEPTION: TypeError: Cannot read property 'test' of undefined

Как я могу это исправить?

Ответ 1

Как и в случае с Angular 2 RC5, DI становится проще. Вам не нужно украшать вещи @Injectable(). Вместо этого вы просто объявляете его для DI в одном месте - NgModule.

export class ErrorHandler {
    test() {
        console.log('ErrorHandler.test()');
    }
}

export abstract class BaseApiComponent<T> {
    // use protected property parameter in abstract class
    // for accessibility from descendants.
    constructor(protected errorHandler: ErrorHandler) {}

    someMethod() {
        this.errorHandler.test();
    }
}

export class ApiComponentImpl<T> extends BaseApiComponent<T> {
    // use @Inject decorator
    constructor(@Inject(ErrorHandler) errorHandler: ErrorHandler) {
        super(errorHandler);
    }
}

app.module.ts:

// class declarations
@NgModule({
    providers: [
        ErrorHandler,
        ApiComponentImpl
    ]
})
export class AppModule{
}

// bootstrap the app
platformBrowserDynamic().bootstrapModule(AppModule);

Вы можете использовать OpaqueToken для улучшения модульности и удаления зависимости типа:

export const errorHandlerToken = new OpaqueToken('ErrorHandler');

в модуле:

    providers: [
        // using OpaqueTokens you can change the provided value
        // to anything without changing consumer code 
        {provide: errorHandlerToken, useClass: ErrorHandler},

Конструктор:

    constructor(@Inject(errorHandlerToken) errorHandler: ErrorHandler) {

Ответ 2

Angular DI поддерживает только конструкторскую инъекцию, что означает, что вам нужен конструктор.

Вы также не можете вводить абстрактный класс непосредственно, потому что абстрактный класс не должен быть инстантабельным.

Поэтому он должен быть следующим:

export abstract class BaseApiComponent<T> {
    constructor(errorHandler: ErrorHandler) {}

    someMethod() {
     this.errorHandler.test();
    }
}

export class ApiComponentImpl<T> {
    constructor(errorHandler: ErrorHandler) {
      super(errorHandler);
    }

}

Ответ 3

Я не разработчик angular, но, глядя на примеры, декоратор @Inject всегда используется как декоратор параметров и а не как декоратор свойств.

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

export abstract class BaseApiComponent<T> {
    private errorHandler: ErrorHandler;

    protected constructor(@Inject(ErrorHandler) handler) {
        this.errorHandler = handler;
    }

    public error() {
        this.errorHandler.test();
    }
}

Также я не уверен, когда вы на самом деле используете this.errorHandler.test();, поскольку он не может просто сидеть там в классе, я переместил его в метод error.


изменить

Right. Вам нужно ввести в расширяющий класс, а затем передать экземпляр родительскому:

export abstract class BaseApiComponent<T> {
    protected errorHandler: ErrorHandler;

    protected constructor(handler: ErrorHandler) {
        this.errorHandler = handler;
    }
}

export class Restaurants extends BaseApiComponent<any> {
    constructor(@Inject(ErrorHandler) handler) {
        super(handler);
    }
}