Angular: Inheritance Without Effort
A great way of using inheritance in our app without maintenance
However, we face a problem when we want services in child components.
Let’s start by creating our
See that the decorator
Component is not necessary — we don’t have to declare this class in the module, as it is not a component. We can also declare it as an abstract class as we would do in regular OOP language.
Then, we create our two components extended by the
When we navigate from one component to another, the logs are correctly printed in the console.
Our child components are now ready!
However, in the
AComponent, I would like to access its
ElementRef (or another service):
And here is our problem. When I want to inject
ElementRef in that component, I have to call the method
super() and pass our two other classes. Imagine, if you have other services in the
BaseComponent, it will be hard to manage the children.
If I add or remove a service in
BaseComponent, I have to reflect these changes in every child component extended by it.
Fortunately, there is a solution which is easy to implement. We will inject our services into the
For this example, I will create another
BaseComponent containing our
Injector — let’s call it
The main change is in the
constructor. Instead of using it to let Angular inject our services, we do this operation manually.
Injector and then I bind every injectable to its corresponding property. And that’s it!
We can now refactor our child component by removing the unnecessary arguments in the
Everything works like a charm. But, we’ll still always have to inject the
Injector… We can do better!
Our goal is to remove the injector from our base constructor but we don’t have access to the instance of
Injector without instantiating it in the constructor.
In fact, we can create our own
Injector when we bootstrap our app and serve it as a singleton. Let’s do this!
Let’s create the
This will keep our custom injector available as a singleton. We set it once in the
We can now use it safely in our base component:
To test if all is working as expected, I will modify the
CComponent and the
And, we also have the
DComponent with the same structure but without
When I navigate from one component to another, the results below demonstrate the working example:
And our children are now empty of these dependencies in the
Exceptions of Inheritance
If you want to inject
ActivatedRoute in your
BaseComponent, it won’t work properly. This class needs to be injected into the component to be correctly instantiated.
However, if you want the route state, I recommend you take a look at the Redux pattern instead of using inheritance, it will make your life easier.
Here is an example for NGXS.
ElementRef, declared in the component’s
constructor, provides the host element. In the abstracted component, it won’t be available — you will get
No provider for ElementRef even if you use your injector.
So, if you need it, you will have to pass it in your child constructor.
Warning: use with caution
The pattern used in this example is known as the ServiceLocator pattern.
It is known as an anti-pattern, so keep in mind to ONLY use it in base components. For example, FormBaseComponent, BaseComponent,… UI components which don’t care about these dependencies.
More info about the ServiceLocator Pattern: https://en.wikipedia.org/wiki/Service_locator_pattern
We have now an excellent way of using inheritance in our app with no maintenance. Inheritance is great to avoid repeatable actions in our components and we can build a smart component if we use it wisely.
However, it might not be the best solution for everything — think about it twice before designing your hierarchy.
Have a nice day!