Supamiu
Supamiu

Reputation: 8731

Inherit Dependency injection

I want to create a generic api service in order to make model-related services easier to create:

export abstract class ModelService<T> {

    constructor(protected apiService: ApiService) {
        //ApiService is a service that wraps Http and adds signature , auth and serialization.
    }

    public getAll(): Observable<T[]> {
        return this.apiService.get<T[]>(this.modelClass, this.baseUrl);
    }
}

then, I will create my service that manages model Foo:

@Injectabe()
export class FooService extends ModelService<Foo>{

}

But when I call fooService.getAll().subscribe(...); I get an error saying that apiService is undefined (cannot get property 'get' of undefined).

So the fix is to add a constructor in FooService like this:

constructor(api: ApiService) {
    super(api);
}

But problem is that it's not dev-friendly, nothing tells you to do so and since FooService extends ModelService, it should have the same constructor and therefore the same dependencies.

Is there any way to inherit dependency?

PS: I already tried to add @Injectable() to ModelService, same error.

EDIT: tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": "",
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [
      "es6",
      "dom"
    ],
    "mapRoot": "./",
    "module": "es6",
    "moduleResolution": "node",
    "outDir": "../dist/out-tsc",
    "sourceMap": true,
    "target": "es5",
    "typeRoots": [
      "../node_modules/@types"
    ]
  }
}

package.json:

"dependencies": {
    "@angular/common": "2.2.1",
    "@angular/compiler": "2.2.1",
    "@angular/core": "2.2.1",
    "@angular/forms": "2.2.1",
    "@angular/http": "2.2.1",
    "@angular/platform-browser": "2.2.1",
    "@angular/platform-browser-dynamic": "2.2.1",
    "@angular/router": "3.2.1",
    ...
    ...
  },
  "devDependencies": {
    "@angular/compiler-cli": "2.2.1",
    ... 
    ...
  }

Upvotes: 2

Views: 400

Answers (2)

Estus Flask
Estus Flask

Reputation: 223104

For child class that doesn't introduce extra dependencies and doesn't need its own constructor it is

@Injectable()
export abstract class ModelService<T> {...}

export class FooService extends ModelService<Foo>{

}

@Injectable() decorator is needed for parent class (if it uses type annotation and not @Inject). No @Injectable() decorator is needed for child class.

For children classes that may have their own dependencies and thus need own constructors the dependencies should be explicitly passed to super (see the other answer). In JavaScript, rest operator and @Inject may be used to skip some tautology, but for TypeScript it is safe to enumerate all constructor parameters explicitly.

Upvotes: 2

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 658057

The constructor needs to repeat the dependencies and then passed to the super class using super(...)

@Injectabe()
export class FooService extends ModelService<Foo>{
    constructor(apiService: ApiService) {
      super(apiService);
        //ApiService is a service that wraps Http and adds signature , auth and serialization.
    }
}

This is not an Angular2 requirement or limitation, that's how constructors work in TypeScript.

Upvotes: 5

Related Questions