Angular dynamic components with code-splitting
Tags: angular, typescript, javascript, webcomponents
Published: 2021-08-03 15:53:16

{% github anirbmuk/cms-components-demo no-readme %}

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.

![CMS content layout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/boaoww1o4dj4y1da1ebh.JPG)

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.

![Lazy loading](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oa9bzx428ap26t8wpro4.JPG)
![Lazy Loading](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rvl1l5b4o133vg9hm9zu.JPG)
&nbsp;

### The Setup
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 structural 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:

![Sample CMS Data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1lqpy0qpsqceggyiums7.JPG)
![Customer Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/35hvxdwdfvxnstdzykpf.JPG)

Now, let's take a look at the custom structural directive:
![Custom Structural Directive](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m7zgnjazfb2q1c05wux.JPG)

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.

#### A small, but very important, GOTCHA!
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.

![tsconfig.app.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uadvua1sauqrsc8fv98b.JPG)

---------------------
Cheers!
[Anirban Mukherjee](https://anirbmuk.appspot.com/en)