Dynamic Content in Angular

In this article, I am going to show you several ways of creating dynamic content in Angular. You will get examples of custom list templates, dynamic component creation, runtime component and module compilation. Full source code will be available at the end of the article.

You can get more practical information on the Angular development in the agile book I’m working on: Developing with Angular.

List item templates

We are going to see how your Angular components can be enriched with custom templating support. We will start by building a simple list component that supports external row templates declared by the application developer.

List component

First, let’s create a simple list component to display bound items collection:

Now update your main application component or create a separate demo component “tlist.component.demo.ts” like in the following example:

This will render an unordered HTML list like this:

angular-dynamic-content-01

Row templates

So we got a simple list component that binds to an array of objects and renders standard unordered HTML list where every list item is bound to the “title” property value. Now let’s change the code to provide support for external templates. Update the code of the “tlist.component.ts” file as shown below:

Now TListComponent expects a template reference to be defined as its content child. It will then take template content and apply to each *ngFor entry. So application developers that are using this component will be able to define entire row template like following:

Now update the “tlist.component.demo.ts” like in the example below:

In order to access underlying data-binding context for each row, we map it to the “item” variable by means of the let-item=”$implicit” attribute. So the “item” will point to an entry in the “items” collection of TListComponentDemo and will be able binding to the “title” property. Additionally we assign row index property value to the “i” variable via let-i=”index”.

Another improvement is that TListComponent no longer enforces all bound objects to have a “title” property. Now both the template and underlying context are defined at the application level.

Here’s how the result will be rendered with the changes made:

angular-dynamic-content-02

Typical use cases

Imagine cases when your Angular components have complex layouts hidden from application developers but at the same time provide a great level of customisation by means of custom templates.


Dynamic Components

Another common scenario is changing the content of the component based on some condition. For example rendering different child component based on the value of the “type” property:

<component type="my-type-1"></component>
<component type="my-type-2"></component>

Let’s start with the basic component structure:

Note the “container” usage. It will be used as injection point, all dynamic content will be inserted into the DOM below this element. There’s also a property of the ViewContainerRef type to allow you accessing container from code.

This component can be later used like following:

<dynamic-content type="some-value"></dynamic-type>

Now let’s introduce two simple components to display based on “type” value and one additional fallback component for “unknown” types.

You will also need a “string” to “type” mapping to be able converting component to the corresponding string. This may be a separate injectable service (recommended) or part of the component implementation:

private mappings = {
   'sample1': DynamicSample1Component,
   'sample2': DynamicSample2Component
};
getComponentType(typeName: string) {
    let type = this.mappings[typeName];
    return type || UnknownDynamicComponent;
}

For a missing type name, the UnknownDynamicComponent will be returned automatically.

Finally, we are ready to create components dynamically. Here’s the simplified version of the component with main blocks of interest:

Please note that every component you are going to create dynamically must be registered within the “entryComponents” section of your module:

@NgModule({
  imports: [...],
  declarations: [...],
  entryComponents: [
    DynamicSample1Component,
    DynamicSample2Component,
    UnknownDynamicComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

You can now test all 3 cases:

<dynamic-content type="sample1"></dynamic-content>
<dynamic-content type="sample2"></dynamic-content>
<dynamic-content type="some-other-type"></dynamic-content>

Runtime Context

In most of the cases, you will probably want to pass some runtime context to newly created child components.

The easiest way to maintain different types of dynamic components is creating a common interface or abstract class. For example:

abstract class DynamicComponent {
   context: any;
}

For the sake of simplicity, I was using “any” type for the “context“, for real-life scenarios you may want declaring the type to benefit from static checks.

All previously created components can now be updated to take context into account:

And dynamic component needs to be updated as well:

export class DynamicContentComponent implements OnInit, OnDestroy {
  ...

  @Input()
  context: any;

  ...
  ngOnInit() {
    if (this.type) {
      ...
      let instance = <DynamicComponent> this.componentRef.instance;
      instance.context = this.context;
    }
  }
}

With the changes above you are now able binding context object from within parent components. Here’s a quick demo:

At run-time, you now should be able to see three components (including fallback Unknown one). Upon changing the text in the Context input box all widgets will be automatically updated.

angular-dynamic-content-03

Typical use cases

Dynamic forms and form persistence is the best example. If you need displaying form (or composite component) based on a definition file (JSON, XML, etc.) you may end up having a dynamic component that builds final content based on the schema and/or persisted state, and a form component built from multiple dynamic content containers.


Runtime compilation

For some advanced scenarios, you might want taking full control over Angular component/template compilation.

In this walkthrough we are going to implement the following features:

  • allow user defining a component template
  • compile Component on the fly (user-defined template + class)
  • compile NgModule on the fly (with component created)
  • display newly created component

The implementation can be pretty much based on Dynamic Component from the previous walkthrough. As a start you will need a basic component with a dedicated placeholder to inject content:

We are going to allow users editing the component template, so let’s add a basic UI for that:

Note that to use ngModel you will need importing and referencing FormsModule within your AppModule:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [...],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

When rendered it should look like following:

angular-dynamic-content-04

Now the most important part of the component implementation, the runtime compilation:

 

The code above takes custom metadata and optionally a component class. If no class is provided the fallback RuntimeComponent one will be used with a name property predefined. This what we’ll be using for testing. Then resulting component gets decorated with the metadata provided.

Next, a RuntimeComponentModule module is created with predefined CommonModule import (you may extend the list if needed) and decorated component created earlier as part of the “declarations” section.

Finally, the function uses Angular’s Compiler service to compile the module and included components. The compiled module provides access to the underlying component factories and this is exactly what we needed.

For the last step we need wiring Compile button with the following code:

Every time user clicks Compile button component takes the template value, compiles new component of it (backed by the RuntimeComponent class with the predefined “name” property) and renders:

angular-dynamic-content-05

Typical use cases

The best option if you want storing component templates somewhere externally and building components on the fly (RAD environments, online creators, etc.)


Source Code

You can get all source code and a working example project from this GitHub repository.

You can get more practical information on the Angular development in the agile book I’m working on: Developing with Angular.

10 thoughts on “Dynamic Content in Angular

  1. Great Article!!! very deep…
    Thanks a lot Denys Vuika!

    I am creating a dynamic wizard and I will probably use your dynamic content/context part
    Joseph Salloum Thanks for the live demo!

    Can I Email you if i’ll need some help with other stuff?

    And again Thanks!

    Like

  2. Hi,

    thanks a lot for this post, it increased my understanding of Angular like no other read.

    I’m currently using the dynamic compilation solution you proposed.
    It’s working fine with JIT, but with AoT it does not (as there is no dynamic compiler available).

    Do you have any suggestion on how to implement a solution that allows for components to have their template fetched dynamically?

    We need to display dynamic forms fetched via HTTP and bind the form’s input to a component’s data.

    Like

    1. Hi Francesco,

      I am about to publish a new chapter on building the Plugin Frameworks with Angular in the next day or two. The chapter should feature many different scenarios that might be of your interest. Feel free to grab the book here: https://leanpub.com/developing-with-angular. All updates are automatic and free.

      The AoT/tree-shaking might indeed cause some troubles for dynamic compilation and I will try to investigate possible ways around. My next chapter will be related to Lazy Loading, will try to find time to elaborate on AoT as well.

      Regards,
      Denys

      Liked by 1 person

      1. Hi Denys,

        thanks for the fast reply (didn’t get notified for some reason).

        I’m not sure I understand your response: do you already have a solution in place for the problem I described and it’s gonna be in the PluginFrameworks chapter or do you still need to figure out a solution as well?

        Thank you 🙂

        Francesco

        Like

      2. Hi Francesco,

        The chapter will contain even more scenarios that complement the original blog post, but based on the compiler available.

        I will need to figure out solution for AoT 🙂

        Regards,
        Denys

        Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s