A lot of times, angular components need to be determined and displayed on the fly. A very common use-case is when the components are determined from a content-management-system (CMS). This is particularly useful when we allow the customer to control the content and layout of the page.
Well we do have
*ngIf for that, don't we? Depending on some CMS field value, we can use
*ngIf to determine which component needs to be displayed. This is cool, but there is a major downside to this approach. The problem is, we usually have a large component library, which need to be loaded eagerly in its entirety through one or more
modules. So even if only a fraction of these components may be needed by the customer, we would have to pre-load all of them, which is an unnecessary waste of resources.
So what are we talking about here? How about a way where we load only those components out of our library which are required by the customer? This is where the concept of code-splitting comes into use.
Using code-splitting, we conditionally import the required components at run-time.
Let's see how to go about it. I have created a few simple custom angular components, which are to be conditionally loaded. So, as expected, these components are NOT part of any application module.
We need to remember that the entire operation is driven by CMS data. So we need some mechanism to evaluate this data at run-time to determine the components.
So we would need a
custom structual directive for this. This directive would dynamically create the component instances and inject them into the DOM. Let's first look at the HTML needed at the implementation side, using this directive:
The most interesting bit is the import statement (line 25). Based on the CMS content, I only import that particular module and component. This is a huge advantage in terms of app-performance, since we do not have to load a module unless it is explicitly asked for, much like a lazily loaded route! When I place this directive on a designated location in the DOM (the
ng-container in this case), I get access to that location's reference, through
ViewContainerRef. I make use of angular's
ComponentFactoryResolver API to build an instance of our required component and the viewContainerRef injects this component instance at that designated spot.
Since our custom components are not included in any module, or any eager import statements, the angular compiler will exclude these .ts files from compilation. To forcefully compile them, so that they can be used at run-time, we need to tell
tsconfig.app.json to include these files.