Loading Components Dynamically in Angular
Dynamic Components in any framework/library make building large-scale apps way easier.
Imagine we have a huge module that consists of multiple components. Every user has unique needs, meaning that they will only use a subset of all the available components.
So it doesn’t make sense to load the whole module for every user, right? The components should be loaded on demand, only if they’re used. The goal of this article is to do exactly that.
To make things easier, let’s consider a use-case. We have an application with role-based user access. Each type of user has a different profile page and can perform different actions based on their type.
The Problem
Now how do we show a different Component for a different type of user?
One thing that we can do is use the ngIf
structural directive to conditionally show a specific Component based on the user type.
But this would still require us to download both the component for every type of user. That does not make any sense, right?
Solution
How about loading the required Component dynamically, on-demand?
That way we won’t bloat up the main bundle with both these components and will only download the component that should be rendered for a particular user. I’m sure if you have bundle-phobia, you’d definitely wanna do that 😉.
How? 🤔
Before we start implementing dynamic components in Angular, let’s first understand what they actually are.
The dynamic component is one of the core concepts introduced in Angular. The dynamic component is the component that is created dynamically at the runtime. Angular has its API for loading components dynamically.
Let’s take a look at how we can build dynamic components in Angular. To implement dynamic component loading in our Angular App, we’ll need:
- Components that need to be loaded dynamically.
- Service to load and resolve the Component dynamically.
- Directive to handle viewContainerRef.
Tl;DR:
- Create Guest Card and User Card Component
- Implementing Service to Load and Resolve Component
- Creating Profile directive and Profile component
- The final step, Updating App component and App module🤓
- Final words 🧡
Implementation 👩🏻💻
It’s time to have fun. Let’s start with the following steps:
- Open cmd & Run the command
ng new dynamic-component
- Open the new project in your editor of choice.
Or if you just wanna follow along, start with this Sample StackBlitz.
Let’s start coding 💪
1. Create Guest Card and User Card Component
As we totally see from the image above, we need two Components, a card for the normal guest profile and one for the registered user.
Let’s create 2 new Components GuestCardComponent
& UserCardComponent
ClientProfileComponent as following:
First, we will create GuestCardComponent
.It’s just a simple Card that shows the user some information about the App and temps them to sign-in for more features.
Now let's create UserCardComponent
It’s just a simple card that shows the information about the user profile.
2. Implementing Service to Load and Resolve Component
In the service, we will implement a method to load and resolve components according to the given state.
In this service, we will start by creating a Subject
to manage the isLoggedIn
state, and two methods login
and logout
to pass events into the subject.
And finally, a magical method🎩
loadComponent
method takes a ViewContainerRef
and the isLoggedIn
state. In order to get a reference to our template
element in the my-app
component we will use ViewContainerRef.
ViewContainerRef :
As the name suggests, its a reference to a container. ViewContainerRef
stores a reference to the template
element (our container that would host the dynamically loaded component ) and also exposes an API to create components.
Before we proceed to the createComponent()
method from ViewContainerRef
, we need to add one more service.
The ComponentFactoryResolver
service exposes one primary method, resolveComponentFactory
.
The resolveComponentFactory()
method takes a component and returns a ComponentFactory
.
We can think of ComponentFactory
as an object that knows how to create a component.
And finally, we will just put everything together inside loadComponent
method.
In this method first, we will Clear the ViewContainerRef
. In order to create the component we need to remove the previous view every time, otherwise, it will append more components to the container.
Once ViewContainerRef
is cleared we will add a condition to get component according to isLoggedIn
status, after that we will pass the component toresolveComponentFactory()
method.
After that, we are calling the createComponent()
method. Internally this method will call the create()
method from the factory and will append the component as a sibling to our container.
3. Creating Profile directive and Profile component
In order to make the loading of the components easier, we will create a directive that will inject ViewContainerRef
to access the view container of the element that host the dynamic added component.
Let’s create a file src/app/profile/profile-host.directive.ts
:
profile-host.directive.ts
This is just a trick to make it easier to get the ViewContainerRef
we’re looking for.
Now let’s create ProfileComponent
. We will be creating a simple ng-template.
The <ng-template> element is a good choice for dynamic component because it doesn’t render any additional output. we will attach the ProfileHostDirective
inside the template, so we can use the ViewChild
decorator, and get the viewContainerRef
.
In OnInit
method, we are getting the viewContainerRef
, and using the isLoggedIn$
observable from ProfileService
to know every time the isLoggedIn
state changes. Then, using the mergeMap
operator, we call the loadComponent
function that is doing the real magic.
4. The final step, Updating App component and App module🤓
Now since we have implemented our services and components let’s update the AppComponent
and AppModule
.
We will include our ProfileComponent
in AppComponent
template
Now, the only thing we cannot forget to do is add ProfileComponent
, and ProfileHostDirective
to the AppModule
declarations array, let’s do it
— For Ivy disabled Applications —
If Ivy is disabled in the application, for dynamically loaded component and a ComponentFactory to be generated, the component must also be added to the module’s entryComponents as following:
declarations: [
AppComponent,
GuestProfileComponent,
ClientProfileComponent
],
entryComponents: [GuestProfileComponent,ClientProfileComponent],
According to the definition of entryComponents
Specifies a list of components that should be compiled when this module is defined. For each component listed here, Angular will create a ComponentFactory and store it in the ComponentFactoryResolver.
And It’s done ✅ . Our application is ready to load components dynamically.
The source code is available on GitHub. Also, find the code for Ivy disabled application here.
Final words 🧡
I hope that you had as much fun coding this as I did while writing this code. Now you know how to dynamically load components with lazy loading. With this knowledge, we can reduce the main bundle size, and make the experience better for users.
Thanks for reading! 👍