3

I will like to know how to inject a service with multiple dependencies inside an abstract class who will be extended by multiple classes.

In a more efficient way then passing it in all constructor!

I try to create static, but if the service is never instantiated by another, the singleton instance variable will never be assigned

Something like this: (Is just an example)

@Injectable({
  providedIn: 'root'
})
export class AnimalService {

  constructor(private http: HttpClient, private userService: UserService) {}

  countTotalInDB(type): number {
    return this.http.get(...);
  }

  getUserAnimals(userId: number) {
    return this.userService.getUser(userId).animals;
  }

}

abstract class Animal {

  constructor() {}

  public getTotalInDataBase(type): number {
    // How to get a instance of AnimalService ?
    return animalService.countTotalInDB(type);
  }

}

export class Cat extends Animal {

  constructor() {
    super();
  }

  public getTotalInDataBase(): number {
    return super.getTotalInDataBase('cat');
  }

}

export class Dog extends Animal {

  constructor() {
    super();
  }

  public getTotalInDataBase(): number {
    return super.getTotalInDataBase('dog');
  }

}

const doggo = new Dog();

console.log(doggo.getTotalInDataBase());

In this case, AnimalService will use HttpClient and UserService.

UserService will use a lot more services.

So how can I get a class instantiation who look like this const doggo = new Dog(); who will create/use/inject the AnimalService without passing it in all classes?

marcXandre
  • 2,072
  • 2
  • 13
  • 21
  • I'm not sure there's a way around this. I would probably create a service that's essentially a factory for instantiating those objects. The service can then pass all the dependencies. – Frank Modica Oct 17 '18 at 15:38
  • How will you do this in an efficient way? Or do you have docs? Links? – marcXandre Oct 17 '18 at 15:40
  • I just mean create a service that has methods like `createDog` etc. That service can inject `AnimalService` etc. It can then do `return new Dog(this.animalService)`. You'd still be passing to the constructor, but I don't see what's inefficient about that. Avoiding `new` like this in your component might actually be beneficial - you can mock `createDog` in your unit tests. – Frank Modica Oct 17 '18 at 15:44
  • Did you find a way to achieve this in the end? – chateau Jan 25 '19 at 22:47
  • Yes ! I'll answer to my question. – marcXandre Jan 28 '19 at 04:11

1 Answers1

8

I finally find how to do this.

Following my example:

import { inject } from '@angular/core'; // Answer

@Injectable({
  providedIn: 'root'
})
export class AnimalService {

  constructor(private http: HttpClient, private userService: UserService) {}

  countTotalInDB(type): number {
    return this.http.get(...);
  }

  getUserAnimals(userId: number) {
    return this.userService.getUser(userId).animals;
  }

}

abstract class Animal {

  protected animalService: AnimalService; // Answer

  constructor() {
    this.animalService = inject(AnimalService); // Answer
  }

  public getTotalInDataBase(type): number {
    // How to get a instance of AnimalService ?
    return this.animalService.countTotalInDB(type);
  }

}

export class Cat extends Animal {

  constructor() {
    super();
  }

  public getTotalInDataBase(): number {
    return super.getTotalInDataBase('cat');
  }

}

export class Dog extends Animal {

  constructor() {
    super();
  }

  public getTotalInDataBase(): number {
    return super.getTotalInDataBase('dog');
  }

}

const doggo = new Dog();

console.log(doggo.getTotalInDataBase());

It works in my case, hope it will help you too!

marcXandre
  • 2,072
  • 2
  • 13
  • 21
  • 4
    Hello @The224, Your code is exactly the structure I desire but I get the error `inject() must be called from an injection context` with `@angular/core: ^6.1.10`, what version would you have developed with? – Scuzzy Apr 02 '19 at 02:31
  • 1
    Are you doing the injection inside a service with the `@Injectable` annotation? I was using angular ~6, the answer also works with 6+. – marcXandre Apr 03 '19 at 16:50
  • Thanks for checking out my message @The224, I had tested by copy/pasting your sample code into my project (and just dummy'ed the countTotalInDB/getUserAnimals responses) to test if I could inject `AnimalService` into the `Animal` class – Scuzzy Apr 03 '19 at 21:56
  • @Scuzzy I had the same issue, here you can find a few solutions https://github.com/angular/angular/issues/25813 and here https://stackoverflow.com/questions/51485868/inject-must-be-called-from-an-injection-context – Arnold Vakaria Mar 27 '23 at 14:52