Dynamically Add Components in Angular

Dave Bush
5 min readApr 25, 2017

--

This past week, I ran into a need to dynamically add components using Angular. My specific reason for doing this is that the component I am using is a port of an AngularJS component and so it doesn’t quite conform to the Angular way of doing things. Specifically, I can’t change input values and have the component respond reliably. But, regardless of why I need this functionality, or even why you may need this functionality, I think being able to do this at all is pretty cool.

Photo via Visualhunt.com

Prerequisites

For the purposes of this post, I’m going to assume that you have a project that you’ve created using the Angular CLI (I’m using version 1.0.0).

To start, we are going to strip out the contents of app.component.html so we have an empty component.

The Basics

If you’ve looked into this subject before, one of the first places you may have landed is the discussion labeled “Dynamic Component Loader” where they discuss dynamically adding an ad component.

The simplified, all you really need to know, version of this is as follows.

Before you create a component dynamically, you need to retrieve the component factory for it. And in order to get the factory, you’ll need a reference to a ComponentFactoryResolver object, which you can inject in your component constructor.

So, the first thing we need to do is that we need to inject the ComponentFactoryResolver into our app component.

When we are ready to add the component, we will use the resolver to create the component. But before we do that, we need a component. For our purposes, create a basic component that does nothing using ng g component dynamic. This should create a component in your dynamic directory.

While the component was added to your declarations section of your app module, we will need to manually add an entryComponents section which specifies this component. Otherwise, it won’t be able to be loaded during runtime.

For the purposes of explaining, we are going to create the component during the afterViewInit event. You can’t create it before this point because we are going to need to retrieve a reference to the host component. Regardless of where you create the component. The steps are the same.

  1. Use the factory resolver to get the component factory
  2. Get a reference to the host container.
  3. Create the component using the host reference.

To get a reference to the host container, we’ll need to inject it into our constructor. So, the resulting app component code will look like this:

If you’ve been following along, you should be able to run the code now and you will see “dynamic works!” when you view it with the browser. But, all is not right.

You see, containerRef.createComponent(componentFactory) actually inserts the component parallel to the host container, not in it as you might expect. So, instead of the component showing up inside of <app-root> it shows up below it.

Besides, even it it had worked, you would normally want to place this new component inside some other DOM elements. How do we fix this?

What you want to do is you want to place an ng-template tag where you want the dynamic component to go and access the viewContainerRef of off it.

You will notice that we gave the ng-template a variable which we will reference in our TypeScript file using @ViewChild(). We can remove the ViewContainerRef injection, and we’ll replace the retrieval of the viewContainerRef with the viewContainerRef from ng-template.

Here is a little trick you might miss. While the tutorial on the Angular.io site has you creating a directive, we are going to skip that part and add a second parameter to our ViewChild directive telling it to retrieve the viewContainerRef from the ng-template control instead of the control. It is a lot less code!

Passing Data

Passing data into your component is where things get a little cloudy. While you can set the properties directly once you have an instance. Some components we work with don’t always cooperate. So, I’m going to present several ways of passing data in. You can assume that handling events works in a similar fashion. Unfortunately, the one way you can’t pass data is by passing a parameter to the createComponent function. Someday? Maybe?

Let’s start by adding an input property to the dynamic component.

And we will display it on the screen using the template.

If you run the code now, nothing will display because we have not yet set the value.

In an ideal world, the component you are creating dynamically would not need to have the Input properties set during the creation process. In this case, all you will need to do is to grab the instance of the component you created, and set the value(s). Because of change detection this works pretty well.

To make this work in our code, we just need to grab the instance and set the property.

And, when you run the code, you see “Hello World”.

Like I said, this is the ideal. But when that doesn’t work, you still have options.

In my case, I created a component wrapper. The only reason that it exists is to wrap the component so I can send in the expected data. The component wrapper is what I create and it retrieves the data it needs from some global location so that it is available when the real component I need to create ask for it.

Global data should be something you can retrieve via injection. In my case, I use NgRX/Store and retrieve what I need via that mechanism. The other, less desirable, mechanism would be to set a static variable and retrieve it that way. But if you have multiple components you need to create, this could become problematic very quickly.

Conclusion

As I mentioned already, I’ve yet to find a way to pass data during the creation of the dynamic component. If you know of a way to do this, PLEASE let me know in the comments.

--

--

Dave Bush

Dave Bush is an Agile/Scrum/Extreme Architect and Programmer who is currently focused on the world of Angular.