Juri Strumpflohner

Juri is a full stack developer and tech lead with a special passion for the web and frontend development. He creates online videos for Egghead.io, writes articles on his blog and for tech magazines, occasionally speaks at conferences and holds training workshops.

Learn about advanced topics such as dynamic components, ComponentFactoryResolver, ViewContainerRef, ngTemplateOutlet and much more...

The purpose of this article is to learn about the concept of dynamic components and template references in Angular by building a dynamic tab component. We will learn about ComponentFactoryResolver, ViewContainerRef, ngTemplateOutlet and ngTemplateOutletContext.

Contents are based on Angular version >=4

Update for Angular version 5: I just upgraded the article to match Angular version 5. You need to change ngOutletContext to ngTemplateOutletContext. Here's a very helpful app for upgrading btw: Angular Update Guide

Egghead.io - Video Course

The setup

This article is based on a previous one I’ve written where I built a tab component by leveraging Angular’s @ContentChildren and content projection. For a better understanding of this article here, I suggest reading that one first:

As you can see, it is already possible to define tabs in a static manner, by using the my-tab component placed inside the <my-tabs>.
We now want to extend this API in such a way that allows us to dynamically open new tab pages based on the user interaction. Assume for example that we have a first statically defined tab which contains a list of people.

Our setup: tab with people list

Whenever we click the “Add new person” or “edit” button beneath an existing record, we want the editing to take place in a separate tab pages that gets added dynamically.

Our setup: editing a person from the list in a dedicated tab

TL;DR - Gimme a running code example!

Impatient? Here’s a Plunk with the fully working example we are going to build step by step in this article. Have fun!

Desired API for our dynamic tabs

So how does the API of our dynamic tabs look like. First, we need to be able to define the content of our dynamic tabs. We do this in the view of our AppComponent where we also have our <my-tabs> component. To define the so-called template of our dynamic tab we want to display, Angular gives us the <ng-template> tag.

<ng-template>
Hi, I'm the content of a dynamic tab.
</ng-template>

So far so good. Remember the example we wanna build? So whenever the user clicks on that “edit” or “Add new person” button, obviously some handler in our component code is triggered. Inside there, we want to be able to invoke the creation of the new tab, something like this:

Note that we added a template variable to our <ng-template> from before. Within our AppComponent we can now reference our tabs component (<my-tabs>) as well as the ng-template by using Angular’s @ViewChild decorator.

We grab the template by its name which we defined in the view, namely “personEdit”, while we can reference the TabsComponentdirectly by its type, which is obviously much more convenient.

Great, we now have all necessary variables to be passed to our openTab(...) function.

Define an Anchor point

Our plan is to dynamically inject new TabComponent instances into the TabsComponent. To do so we first of all have to define a region in our tab component where to place our dynamic tab pages. Currently our statically define ones are being loaded in via Angular’s content projection mechanism.

We only need this one for the purpose of getting hold of the ViewContainerRef later. By injecting it in the constructor of our directive and by using the public modifier, we can later simply access it through that viewContainer variable.

Alternative: use a template variable as anchor

Instead of creating a dedicated anchor directive there is also the possibility to use a simple template variable and get its according view container via the @ViewChild decorator.

dynamicTabPlaceholder directly references the ViewContainerRef instance which we can use right away.

Both approaches can be used as there are no major advantages or disadvantages. In this article we will make use of a dedicated anchor directive.

Dynamically instantiate the Tab component

Remember, each of our tab pages inside our tabs component is defined using the <my-tab> component. Normally the tab is defined within the template of some component like

<my-tabs><my-tab[tabTitle]="..">The content</my-tab></my-tabs>

However, in our scenario we want to instantiate the <my-tab> in our code dynamically and set the according properties of it. More specifically we instantiate the underlying component class TabComponent.
For that purpose we need to get hold of a component factory matching our TabComponent. It’s as simple as injecting the ComponentFactoryResolver and then invoking its resolveComponentFactory(…) function passing in the type of component we want to instantiate: the TabComponent.

Next, we need to get a ViewContainerRef which is where our dynamically instantiated component will be placed. We can get that view container from our anchor component as we just discussed (or via the alternative approach of using a template variable).

Once we have the component factory and the view container reference, we can finally instantiate our component by using the createComponent(…) function on the view container. Having the instance of the TabComponent we can then set its according properties.

openTab(title,template,data,isCloseable=false){letcomponentFactory=this._componentFactoryResolver.resolveComponentFactory(TabComponent);// get the viewcontainerletviewContainerRef=this.dynamicTabPlaceholder.viewContainer;// instantiate the componentletcomponentRef=viewContainerRef.createComponent(componentFactory);letinstance:TabComponent=componentRef.instanceasTabComponent;// set the propsinstance.title=title;instance.template=template;instance.dataContext=data;instance.isCloseable=isCloseable;...}

Render the dynamic template with NgTemplateOutlet

Last but not least we need to render the template in our dynamically created tab page. Remember, in our openTab(…) function we did pass in the template reference this.editPersonTemplate…

You may have noticed that in the implementation of the openTab(…) function we pass that template along to the TabComponent:

openTab(title,template,data,isCloseable=false){...letcomponentRef=viewContainerRef.createComponent(componentFactory);letinstance:TabComponent=componentRef.instanceasTabComponent;// set the props...instance.template=template;...}

Inside there we can now render the template into the view of our TabComponent. Currently the TabComponent is defined as follows:

Associate data to dynamic templates with NgTemplateOutletContext

Obviously a template without some dynamic data is quite boring. In our example, we need to be able to pass in the person object we’re editing. So let’s take a closer look at our <ng-template> definition:

Inside the template the <person-edit> component takes care of editing our person record by creating a proper form with bindings, events etc.

Note: this is not scope of this article, but feel free to check out its implementation in the linked Plunker

The person-edit component

The component takes an input binding [person] and has an outbound event (savePerson). Let’s first look at the simpler outbound one. We can directly hook it to the AppComponent code where our <ng-template> is defined and just implement the event as we normally would. Templates can directly bind to the component where they are defined, although they may end up being rendered inside another component, just as in our case inside the TabComponent. Think about it, this is really powerful.

But what about our input binding [person]="person"? We could simply create a member variable person inside our AppComponent and assign it whenever we click on some person to edit. Sure, fair enough. But that wouldn’t allow us to edit multiple people simultaneously, such as:

Editing in multiple tabs at the same time

Note, we have a tab open editing the person “Juri” while at the same time creating a new person record. As such, each template needs to get its own specific data. Luckily enough, there’s a way to do so.

First of all we need to define the variable on our template using the let-person="person" syntax:

Note, we give ngTemplateOutletContext an JavaScript object, which has a property person, which is exactly the one we previously defined: <ng-template let-person="...">.
The dataContext contains the person record that gets passed in via an @Input property to our TabComponent. Take a look at the openTab(…) implementation again.

Register the dynamic component on NgModule

Finally, to make dynamic components work, we need to make sure to register them to the entryComponents array on our NgModule:

Obviously, there are some more details to make the whole tab component work properly, such as keeping track of the dynamic components in an array to render the tab headers etc. They don’t really matter for the purpose here of learning dynamic components, but obviously feel free to check the source code.

Destroy a dynamic component

Well, whenever you’re creating something, especially in a dynamic way, make sure to also think about how to destroy it again. In our example, the user has the possibility to close a tab by clicking the “X” on the tab header. Moreover whenever the save button is clicked, the tab also closes automatically.

Click the X to close the tab and destroy the component

Fortunately, the ViewContainerRef has a function remove(idx) which allows to remove a previously added component via an index.

Also take a look at the official docs for ViewContainerRef: https://angular.io/api/core/ViewContainerRef

The running example

Check out the full example in this Plunker here:

Conclusion - Wrapping up

Phew, that was quite long. Dynamic components in Angular initially seem like a mystery to many people. It’s not as easy as in AngularJS (version 1.x) where you could simply render in a DOM string containing a bunch of directives/components and they would automagically load up. Angular takes a more verbose approach as you have seen here. We talked about component factories, view containers and passing template references around. But it’s for a good purpose and once you played around with it, it isn’t that difficult after all. The main reasoning behind is to get static analysis by the Angular compiler. As you might have spotted, we used a well defined class, like the TabComponent, to get a dedicated factory for creating such components. Also, by having a view container, the compiler “knows” where your components will get placed. All this information helps the compiler to better reason about the application structure and thus to provide static analysis for better dev support, security and ultimately better performance.

Other examples
Although this example is fully working, it was created for an academic purpose if you want, to learn more about dynamic components, view containers and how to pass around templates rather than to create a feature blown production read tab component. Of course, you’re free to use it if you want, but there are probably more feature complete ones out there already available for you. Here are some examples: