Home > other >  Extending a class that has injected services without duplicating injectors in child class?
Extending a class that has injected services without duplicating injectors in child class?

Time:02-11

Ok, so I'm getting deep into TypeScript inheritance (specifically in Angular 11), and I've set up a BasePageComponent to provide all the necessary functions and services that exist across pages. The issue I've run into, is that now my base is getting bloated, because there are services that are used only on one or two pages, but in order to extend the base, we have included those in the base.

export class BasePageComponent {

  constructor(
    public uiPopupService: UiPopUpService,
    public loaderService: LoaderService,
    public questionService: QuestionService,
    public aBunchOfOthers: OTHERS,
    public exampleOneOffService: ExampleOneOffService
  ) {}
}

I realize that it would be more performant to repeat all the injected services, and then pass them all up to BasePageComponent as so:

export class ChildPageComponent extends BasePageComponent {

  constructor(
    public uiPopupService: UiPopUpService,
    public loaderService: LoaderService,
    public questionService: QuestionService,
    public aBunchOfOthers: OTHERS,
    private exampleOneOffService: ExampleOneOffService
  ) {
    super(uiPopupService, loaderService, questionService, aBunchOfOthers);
  }
}

I will do the above, if there's no other way, but I have as hinted at in the sample code, I have a bunch of other micro services that are included in the base (like 15-20), so I want to be able to do something like this, where I just declare the one additional service, and still inherit all the others from the parent class, but I am not seeing a way to do this.

export class ChildPageComponent extends BasePageComponent {

  constructor(private otherExampleService: OtherExampleService){
    super() 
  };
}

Does this pattern exist and I'm just not googling the right keyword?

CodePudding user response:

This is not how the DI works.

The DI will provide to you the parameters of your constructor. It has no knowlegde of the parent class.

The parent classes will get nothing from the DI if it's called from a subclass. In this case, the parameters are passed via super()

CodePudding user response:

This is kind of ironic, you're using inheritance to avoid dependency injection, when in reality you should be using dependency injection to avoid inheritance. If components have shared services, all you need to do is put the ones you need in the constructor and you're done.

If they have shared functions, you can put the functions in a service. If they have shared variables, you can use services to pass data around. The point is to have components as their own separate entities, so if you change one, it doesn't affect any others.

Services follow this same philosophy, because you can change the implementation of the service, without having to touch any files that use the service. Think about your design, what happens if you need to remove or add a service to the base component? You now need to go and edit every other component in your entire project. And when you inject a service into a component it creates an instance of that service too. By injecting all of your services into every component, you're creating countless instances of wasted memory and processing power. And do you actually gain anything in return?

What I'm trying to say is you should try to avoid extending components. Angular provides much better design philosophies.

To answer your question, yes, you could create a global service that imports all of your other services, then inject that one massive service. You could then extend that service and import even more. But for the love of all that's holy please don't.

CodePudding user response:

Thanks to @MikeOne's comment for his link to another answer ( Inject a service manually ).

I was able to use Injector.get for the 15 small services that are used in basically every child component. Then for the larger services and the services that are only used on a few child components, I injected them into the relevant components.

For anyone else that comes here in the future: One snag I ran into is that Angular's ActivatedRoute apparently doesn't work with the Injector.get (see How to retrieve a route param using Injector.get(ActivatedRoute)?), so I had to inject that into each the relevant components, which was a majority of them, but that is still better than 16 one off services being initialized on every page.

  • Related