PART 1 — The Case For Component Inheritance In Angular
Angular is a very Enterprise friendly JS framework. I have spent the past 2 years building Enterprise software in healthcare and every day I solve a difficult problem with Angular, I really get this happy feeling thankful that we stuck with Angular.
The Issue Explained
I found myself building a healthcare application in the mental health industry since 2016. Basically, we have a Psychology application that uses some content we created to help users improve their mental health. Now each of our content is sort of like a course or examination that has some questions for the users to answer. These questions or BLOCKS as we call them are spread out across multiple pages.
Our application has different types of Questions, and there are some things that all Question blocks have in common. Let us start with what each Question block has in common. A typical Question block has a Header & Content.
The header basically exists to act as a title, while the content area houses different types of things. For example, in some Questions, the content is just text for the user to read, but it could also be a Radio-Group, a Video/Picture or even just a TextArea requiring user input.
Typically you would expect that we create a component for each type of Question block that our application supports and this would actually be the right way to proceed. So we did go ahead and create a component for each question type. However, this technique would not solve a very important problem and requirement that our company has. We have a number of clients and each client wants a different look and feel, so we cannot use one component as we cannot change the markup of one component without a really messy template that is coupled with soo much if/else logic. Also, we have cases where a specific client might ask us for a very specific feature that only they themselves want inside their own application alone.
Component Inheritance To The Rescue
This is where arguably, Angular’s least used and discussed feature comes to the fray. What is Component inheritance in Angular and what does it look like? Component inheritance in Angular relies on the built-in concept of inheritance that Object Oriented Programming (OOP) makes available for use VIA the extends keyword.
From the illustrations above, ComponentB will basically now have a property called name even though it doesn’t explicitly have this “name” property. This is how inheritance works but since we are using Angular, there are also some few other benefits we get from component inheritance.
Let us understand some key limitations & features on Angular’s component inheritance system.
- The component only inherits the class logic
- All meta-data in the @Component decorator is not inherited
- Component @Input properties and @Output properties are inherited
- Component lifecycle is not inherited
These features are very important to have in mind so let us examine each one independently.
The Component only inherits the class logic
When you inherit a Component, all logic inside is equally inherited. It is worth noting that only public members are inherited as private members are only accessible in the class that implements them.
All meta-data in the @Component decorator is not inherited
The fact that no meta-data is inherited might seem counter-intuitive at first but, if you think about this it actually makes perfect sense. If you inherit from a Component say (componentA), you would not want the selector of ComponentA, which you are inheriting from to replace the selector of ComponentB which is the class that is inheriting. The same can be said for the template/templateUrl as well as the style/styleUrls.
Component @Input and @Output properties are inherited
This is another feature that I really love about component Inheritance in Angular. In a simple sentence, whenever you have a custom @Input and @Ouput property, these properties get inherited.
You can see that ComponentB is using the name property it inherited from ComponentA
Component lifecycle is not inherited
This part is the one that is not soo obvious especially to people who have not extensively worked with OOP principles. For example, say you have ComponentA which implements one of Angular’s many lifecycle hooks like OnInit. If you create ComponentB and inherit ComponentA, the OnInit lifecycle from ComponentA won't fire until you explicitly call it even if you do have this OnInit lifecycle for ComponentB.
Let us take a look at this phenomenon in practice.
You can see that the Base component which in this case is ComponentA implements Angular’s OnInit lifecycle interface. If you use the ComponentB class, the console message would never be called.
Now let us have a look at how we can fix this.
Calling Super/Base Component Methods
In order to have the ngOnInit() method from ComponentA fire, we need to use the super keyword and then call the method we need which in this case is ngOnInit. The super keyword refers to the instance of the component that is being inherited from which in this case will be ComponentA.
The lifecycle of the superclass is only a function and this function can be called at the developer's discretion and doesn't have to be solely called inside of the ngOnInit of ComponentB.
By now, you have a firm understanding of how component inheritance in Angular works. This article is the first part of a series so be sure to read part 2 to see how we solve the white labelling issue.