A great feature that Angular has is to dynamically load component in a Higher Order Component by using the ComponentFactoryResolver. Apart from this, we can also lazy load a component using ES6 import.
Set the basics
Let's start with the basics, meaning how to load a component using a selector and improve it on the go.
In the image below, we see a parent component with a dashed area, a placeholder for another component to use.
The HTML is simple as well
Let's use the selector in the placeholder
Using the selector to use a component is very basic. I promise you that it will become a bit more complicated and fun at the same time. Keep reading 🐱👓
Load components conditionally
Particular needs force us to use a condition on the presentation of the components.
I love to use the @Attribute decorator when I have to differentiate the component's behavior, and the value is a static one, which means that it doesn't change during the runtime.
In the constructor, we have the @Attribute decorator, which has the name "type" and accepts two values. "lazy" or "lazy1".
The @Attribute decorators are used similar to the @Input decorators, as seen in the code snippet below.
So, in the host element, we are providing the "type". We are now ready to apply our condition based on this injected value.
What if we need to handle many components? We have to re-visit this part of the code as many times as we have new components to load, and this is a violation of the Open Close Principle.
The solution is to lazy load components!
How to Lazy Load components
To lazy load a component, we first have to set a placeholder to make this available to the component.ts code as well. We will create an <ng-container> element with a template reference variable.
With @ViewChild, we request the template reference variable, and we will use it to render a component dynamically in that.
The rest of the ingredients are
The ComponentFactoryResolver generates a ComponentFactory of the provided type of the component.
The ComponentFactory is used to create components dynamically. In a nutshell, it holds the component type we need to create.
Lastly, we have to inject the component factory in the template using the @ViewChild instance. Let's have a look at the code.
And that's the most of it! 🥳 🥳
With the code above, we managed to render the component LazyContentComponent in the lazyContent wrapper element dynamically.
But wait! 🤔
The shown solution doesn't seem to lazy load a component. Yes true. It's just a dynamic loading of a component.
We had to know how to dynamically load a component to move on to the next topic, the fan part.
To lazy load a component, we will need the ComponentFactoryResolver, the ComponentFactory, and to ES6 import our component specifying the path.
The code above loads the component from the path we set in the ES6 import('...'). It provides the class of that component in the ComponentFactoryResolver. Finally, it loads the componentFactory in the container.
Lazy load dynamically based on a pattern path
Earlier in this article, we had a condition to switch between two components. We used the *ngIf directives. We can achieve the same result by lazy loading the components as soon as they keep a consistent pattern in the pathname.
We currently used one component with the following characteristics:
We will create one more component:
We notice that they have the same pattern and changes only the name. The first one has the name lazy, whereas the second has the name lazy1.
With this in mind, the wrapper component, or to better state it, the Higher-Order-Component (HOC), can now switch over those two components.
Let's have a better look at the following.
The ES6 imports return a module object that has a property with the class name of the component. This class name follows Angular's name convention and is capitalized.
Either this lazyContentComponent.LazyContentComponent or this
So, we capitalize the type. From lazy we convert it to Lazy.
What is important to note here is that Angular doesn't know where the path of the component is, and hence cannot compile it. If we try to compile the application we will get some warnings and if we run the application we will also notice that the lazy components do not work as expected.
Since we know exactly what the reason behind this error is, we can make it work :)
We have to manually enter in tsconfig.app.json file the paths of the components we want to have them lazy loaded.
How to use it
A solution like this offers agility as we can create several components without writing a simple line of code in the Higher-Order-Component. This solution respects the Open Close Principle as well.
You can find a working example in this repo https://github.com/profanis/angular-hoc-lazy
Thanks for reading it 🤗