I have abstract class(without constructor) and I want to inject another class into it.
Abstract class:
import { ErrorHandler } from '../../shared/services/errorHandler.service'; import { Inject } from '@angular/core'; export abstract class BaseApiComponent<T> { @Inject(ErrorHandler) errorHandler: ErrorHandler; this.errorHandler.test(); }
Injected class:
import { Injectable } from '@angular/core'; @Injectable() export class ErrorHandler { constructor() { } public test() { console.log('Test'); } }
And I have next error
ORIGINAL EXCEPTION: TypeError: Cannot read property 'test' of undefined
How i can fix this?
Yes, you can use an abstract base class to do the same thing, but I'd only use that if there's a common set of functionality I can factor out into the base class to keep my code DRY.
Abstract classes are good where we have shared code in the implementations. This lets us put the shared code into the abstract class and all of the child classes can use it (thus, no duplication). We are limited by single inheritance, so we cannot descend from an abstract class and another class at the same time.
The D letter in SOLID is the Dependency Inversion principle. It helps to decouple modules from each other so that you can easily swap one part of the code for another. One of the techniques that helps to follow this principle is Dependency Injection.
TypeScript has the ability to define classes as abstract. This means they cannot be instantiated directly; only nonabstract subclasses can be.
As of Angular 2 RC5 the DI becomes simpler. You don't need to decorate the stuff with @Injectable()
. Instead, you just declare it for DI in one place - 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);
You can employ OpaqueToken to improve modularity and remove type dependency:
export const errorHandlerToken = new OpaqueToken('ErrorHandler');
in the module:
providers: [ // using OpaqueTokens you can change the provided value // to anything without changing consumer code {provide: errorHandlerToken, useClass: ErrorHandler},
constructor:
constructor(@Inject(errorHandlerToken) errorHandler: ErrorHandler) {
Angular DI only supports constructor injection, which means you need a constructor.
You also can't inject into an abstract class directly because an abstract class is not supposed to be instantiatable.
Therefore it has to be like:
export abstract class BaseApiComponent<T> { constructor(errorHandler: ErrorHandler) {} someMethod() { this.errorHandler.test(); } } export class ApiComponentImpl<T> { constructor(errorHandler: ErrorHandler) { super(errorHandler); } }
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