Back to the homepage
Angular

Dynamic components – what they are part II

Dynamic components continued: Angular 9 and the dynamic component tree

More than four years ago we published an article about dynamic components in Angular 2. Today we have the Angular 9 version and we would like to keep you up to date and “update” our knowledge.  Dynamic components – what are they? What options does Angular provide when it comes to creating components dynamically? How to build a dynamic component tree and what is it? In this article, we will look for answers to these questions.

Dynamic components

There are already plenty of great articles on dynamic components. In short, a dynamic component is a component whose selector is not used in any template of another component. Instead, it is loaded imperatively through its class and we have to make the extra effort that is done in the background for regular components by the framework itself.

What kind of effort are we talking about?

Let’s assume that we have already written our component. Here is one way to load it dynamically:

  1. Injecting a ComponentFactoryResolver into the component that will load the dynamic component (let’s call it a “loader”)
  2. The old approach with Angular 8 (“maybe” it will be removed in Angular 11):

Adding the dynamic component to the declarations and entryComponents tables of the module that uses it. Then, create a factory for the dynamic component, passing the component type to the resolComponentFactory method of the ComponentFactoryResolver instance.

The new approach with Angular 9:

You don’t actually need to declare the component anywhere, its code won’t be in the initial bundle, but we won’t be able to use useful things,  e.g. CommonModule (there is a clever workaround though, more on that later) because of the lack of module.To get a component factory, use the dynamic import syntax which specifies the path to the module / typescript file where the dynamic component is exported. This import will return a Promise with the module where its freshly loaded class is under the key of the component name. We can then pass it to the resolveComponentFactory method.

3. The next thing will be to find a place in the view for our dynamic component. For this, we need a reference to the ViewContainer. We can get such a reference by first adding our “loader” to the template. And later, by reading the indicated “template reference variable” using ViewChild, specifying that we want to “read” it as ViewContainerRef:

  1. At last, when we use the create method on the ViewContainer, passing our component factory, our dynamic component will be created. This method also returns the reference to our newly created component, which is worth keeping adjustable, via which we have access to component properties or it can be used for its destruction. 

Unfortunately all those steps are inconvenient. Does Angular provide a better, easier proceeding?

Easier, yes, but limited, which only makes it better in certain situations.

NgComponentOutlet

It is a structured directive that hides the complexity of the steps mentioned above. It is used as follows:

We specify which component to load according to its class – in Angular 9 we still need to do a dynamic import to load the chunk with that class. Additionally, we can pass a custom injector (by default it is taken from the viewContainer, overlaid with the directive), and transclude some content into the ng-content of the dynamic component.

So… what are the disadvantages of this solution?

Mainly two:

  • We don’t have access to the component references.
  • We don’t have the possibility to bind inputs/outputs.

To pass some data, we would have to inject a service into the dynamic component, or, more directly, pass an injector with an injectionToken containing that data.

Dynamic component tree

To show a more complex case of dynamic components usage, let us consider a certain problem:

Imagine that our application is a builder in which the user creates some structure from predefined elements. Some of these elements may contain subsequent elements, and some of the subsequent elements may contain subsequent elements (and so on). Furthermore, this structure can be saved on the server-side and then downloaded from API and displayed back to the user.

 

Let’s walk through solving a slightly simplified version of this problem:

  • we have “defined elements”, some pool of dynamic components
  • there is an object that stores the configuration of the dynamic component, the configuration stores the type of the dynamic component and possible configurations of nested components (this is the form the JSON loaded from the backend would potentially take)
  • based on this object, we need to load and display the tree of angular dynamic components

First, let’s create an interface to this configuration object:

This is a recursive data structure that contains two keys:

  • content, an array of configurations if our dynamic component has children
  • type, which is an enum of all possible strings describing the component type

We have four types of dynamic components that we will create later: cmp1, cmp2 … etc. Now we’ll create a utility with a method that returns a locked object of the interface above

From this object we will need to create a tree of dynamic components and display them to the user. For example, we see that the root component is of type “cmp1” and has two children: both of type “cmp2”.

Boilerplate

Let’s generate a boilerplate for components cmp1 to cmp4. In the same file as the component, let’s also add @NgModule, where we declare the component and import the modules, components, directives, etc. which this component uses. The module need not (and should not) be exported. It will be found and compiled because there is a dynamic import pointing to the file in which that module (and component) is contained. We will get a separate chunk from each dynamic component and module file, the important thing for us is that it is not marked as initial:

What’s more, Angular will take care of pulling in the dependencies itself, not duplicating the imports that exist in already loaded chunks. In practice, this means that we should have up to a thousand dynamic components in our application, but the user will only load the ones that are currently used on the view.

SharedModule

In SharedModule we store all the common stuff used by our dynamic components:

We then create an object that will map the strings from the configuration to functions that return the corresponding paths to the components:

Loading dynamic components

Having finished our declarations, we move on to the next part – loading our dynamic components.

Let’s use a struct directive for this, similar to ngComponentOutlet but more adjusted to our needs. It should be declared and exported in SharedModule:

Basically, it’s a procedure similar to the one described at the beginning of this text, except that it is parameterized by an input with a configuration. Based on the type from the configuration, we run the corresponding function with the import in dynamicComponentImportsMap. Then, to find the component class, we look for the first value of the imported module that has the property “ɵcmp” (in Angular 9, the component decorator is transformed to this form). The only unexplained thing left is the DynamicComponentBase. This is an abstract class that will serve us as a common API for our dynamic components. For now, let’s implement one input per configuration array there.

Our components will inherit access to the configuration:

One last step remains to actually use our DynamicComponentLoader directive. In the main component we need to add:

app.component.ts

app.component.html

And in our dynamic components, for recursive loading of potential children:

cmp1.component.html

Final structure

After adding the styles, our structure looks like this:

The whole code is available here.

In the next article we will cover more advanced topics – input / output management, scalability and fix some UX / optimization problems of the current solution.

About the author

Mateusz Doduc

Mateusz is a newbie writer in the blogosphere and an experienced angular developer. He is a fantasy and RPG fan, generally a gamer (PC Master Race!).
He likes to explore flavors of the world, particularly the eastern ones.

Don’t miss anything! Subscribe to our newsletter. Stay up-to-date with the latest trends, tips, meetups, courses and be a part of a thriving community. The job market appreciates community members.

Leave a Reply

Your email address will not be published. Required fields are marked *