By Vern Jensen


2015-09-01 20:48:30 8 Comments

In Angular 1.x, we could insert HTML in real-time by using the HTML tag ng-bind-html, combined with the JavaScript call $sce.trustAsHTML(). This got us 80% of th way there, but wouldn't work when Angular tags were used, such as if you inserted HTML that used ng-repeat or custom directives.

To get that to work, we could use a custom directive that called $compile.

What is the equivalent for all of this in Angular 2? We can bind using [inner-html] but this only works for very simple HTML tags such as <b>. It doesn't transform custom angular 2 directives into functioning HTML elements. (Much like Angular 1.x without the $compile step.) What is the equivalent of $compile for Angular 2?

6 comments

@Gregory Edwards 2018-04-04 14:04:35

I think all you have to do is set the element you want to have compiled html with the [innerHTML]="yourcomponentscopevar"

@sg7 2018-04-04 14:27:52

Could you elaborate on this solution?

@mruanova 2018-09-07 21:56:49

this.yourcomponentscopevar = '<b>text in bold</b>';

@nostop 2017-12-08 14:22:16

Have a look at this module https://www.npmjs.com/package/ngx-dynamic-template

After a long research, only this thing helped me. The rest of the solutions seems to be outdated.

@Daniel abzakh 2016-09-11 01:26:37

DynamicComponentLoader is deprecated, you can use ComponentResolver instead

You could use this directive, add pipes if you need additional data manipulation. It also allows for lazy loading, you don't need it in your case, but it's worth mentioning.

Directive(I found this code and made some changes, you can do that too to make it fit your taste or use it as is):

import { Component, Directive, ComponentFactory, ComponentMetadata, ComponentResolver, Input, ReflectiveInjector, ViewContainerRef } from '@angular/core';
declare var $:any;

export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
    const cmpClass = class DynamicComponent {};
    const decoratedCmp = Component(metadata)(cmpClass);
    return resolver.resolveComponent(decoratedCmp);
}

@Directive({
    selector: 'dynamic-html-outlet',
})
export class DynamicHTMLOutlet {
  @Input() htmlPath: string;
  @Input() cssPath: string;

  constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
  }

  ngOnChanges() {
    if (!this.htmlPath) return;
    $('dynamic-html') && $('dynamic-html').remove();
    const metadata = new ComponentMetadata({
        selector: 'dynamic-html',
        templateUrl: this.htmlPath +'.html',
        styleUrls:  [this.cssPath]
    });
    createComponentFactory(this.resolver, metadata)
      .then(factory => {
        const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
        this.vcRef.createComponent(factory, 0, injector, []);
      });
  }
}

Example how to use it:

import { Component, OnInit } from '@angular/core';
import { DynamicHTMLOutlet } from './../../directives/dynamic-html-outlet/dynamicHtmlOutlet.directive';

@Component({
  selector: 'lib-home',
  templateUrl: './app/content/home/home.component.html',
  directives: [DynamicHTMLOutlet]
})
export class HomeComponent implements OnInit{
    html: string;
    css: string;

    constructor() {}

    ngOnInit(){
    this.html = './app/content/home/home.someTemplate.html';
    this.css = './app/content/home/home.component.css';
    }

  }

home.component.html:

<dynamic-html-outlet [htmlPath]="html" [cssPath]="css"></dynamic-html-outlet>

@Parag Ghadge 2017-09-25 09:16:15

Hi... Above solution is giving error as below, Typescript Error Module '"D:/app/node_modules/@angular/core/core"' has no exported member 'ComponentResolver'. Typescript Error Module '"D:/app/node_modules/@angular/core/core"' has no exported member 'ComponentMetadata'.

@Anshul 2016-03-22 09:58:24

Angular provided DynamicComponentLoader class for loading html dynamically. DynamicComponentLoader have methods for inserting components. loadIntoLocation is one of them for inserting component.

paper.component.ts

import {Component,DynamicComponentLoader,ElementRef,Inject,OnInit} from 'angular2/core';
import { BulletinComponent } from './bulletin.component';

@Component({
    selector: 'paper',
    templateUrl: 'app/views/paper.html'

    }
})
export class PaperComponent {
    constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {

    }

    ngOnInit(){
        this.dynamicComponentLoader.loadIntoLocation(BulletinComponent, this.elementRef,'child');

    }
}

bulletin.component.ts

import {Component} from 'angular2/core';

@Component({
    selector: 'bulletin',
    templateUrl: 'app/views/bulletin.html'
    }
})
export class BulletinComponent {}

paper.html

<div>
    <div #child></div>
</div>

Few things you need to take care of :

  • Don't call loadIntoLocation inside the constructor of class . Component view is not yet created when component constructor is called. You will get error -

Error during instantiation of AppComponent!. There is no component directive at element [object Object]

  • Put anchorName #child in html otherwise you will get error.

Could not find variable child

@Jesús Sobrino 2016-03-21 12:54:33

After reading a lot, and being close of opening a new topic I decided to answer here just to try to help to others. As I've seen there are several changes with the latest version of Angular 2. (Currently Beta9)

I'll try to share my code in order to avoid the same frustration I had...

First, in our index.html

As usual, we should have something like this:

<html>
 ****
  <body>
    <my-app>Loading...</my-app>
  </body>
</html>

AppComponent (using innerHTML)

With this property you will be able to render the basic HTML, but you won't be able to do something similar to Angular 1.x as $compile through a scope:

import {Component} from 'angular2/core';

@Component({
    selector: 'my-app',
    template: `
                <h1>Hello my Interpolated: {{title}}!</h1>
                <h1 [textContent]="'Hello my Property bound: '+title+'!'"></h1>
                <div [innerHTML]="htmlExample"></div>
             `,
})

export class AppComponent {
    public title = 'Angular 2 app';
    public htmlExample = '  <div>' +
                                '<span [textContent]="\'Hello my Property bound: \'+title"></span>' +
                                '<span>Hello my Interpolated: {{title}}</span>' +
                            '</div>'
}

This will render the following:

Hello my Interpolated: Angular 2 app!

Hello my Property bound: Angular 2 app!

Hello my Interpolated: {{title}}

AppComponent Using DynamicComponentLoader

There is a little bug with the docs, documented in here. So if we have in mind that, my code should look now like this:

import {DynamicComponentLoader, Injector, Component, ElementRef, OnInit} from "angular2/core";

@Component({
    selector: 'child-component',
    template: `
        <div>
            <h2 [textContent]="'Hello my Property bound: '+title"></h2>
            <h2>Hello my Interpolated: {{title}}</h2>
        </div>
    `
})
class ChildComponent {
     title = 'ChildComponent title';
}

@Component({
    selector: 'my-app',
    template: `
                <h1>Hello my Interpolated: {{title}}!</h1>
                <h1 [textContent]="'Hello my Property bound: '+title+'!'"></h1>
                <div #child></div>
                <h1>End of parent: {{endTitle}}</h1>
             `,
})

export class AppComponent implements OnInit{
    public title = 'Angular 2 app';
    public endTitle= 'Bye bye!';

    constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {
//        dynamicComponentLoader.loadIntoLocation(ChildComponent, elementRef, 'child');
    }

    ngOnInit():any {
        this.dynamicComponentLoader.loadIntoLocation(ChildComponent, this.elementRef, 'child');
    }
}

This will render the following:

Hello my Interpolated: Angular 2 app!

Hello my Property bound: Angular 2 app!

Hello my Property bound: ChildComponent title

Hello my Interpolated: ChildComponent title

End of parent: Bye bye!

@alexpods 2015-09-01 21:57:29

In Angular2 you should use DynamicComponentLoader to insert some "compiled content" on the page. So for example if you want to compile next html:

<div>
    <p>Common HTML tag</p>
    <angular2-component>Some angular2 component</angular2-component>
</div>

then you need to create component with this html as a template (let's call it CompiledComponent) and use DynamicComponentLoader to insert this component on the page.

@Component({
  selector: 'compiled-component'
})
@View({
  directives: [Angular2Component],
  template: `
    <div>
      <p>Common HTML tag</p>
      <angular2-component>Angular 2 component</angular2-component>
    </div>
  `
})
class CompiledComponent {
}

@Component({
  selector: 'app'
})
@View({
  template: `
    <h2>Before container</h2>
    <div #container></div>
    <h2>After conainer</h2>
  `
})
class App {
  constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
    loader.loadIntoLocation(CompiledComponent, elementRef, 'container');
  }
}

Check out this plunker

UPD You can create component dynamically right before the loader.loadIntoLocation() call:

// ... annotations
class App {
  constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
    // template generation
    const generatedTemplate = `<b>${Math.random()}</b>`;

    @Component({ selector: 'compiled-component' })
    @View({ template: generatedTemplate })
    class CompiledComponent {};

    loader.loadIntoLocation(CompiledComponent, elementRef, 'container');
  }
}

I personally don't like it, it's look like a dirty hack to me. But here is the plunker

PS Beware that at this moment angular2 is under active development. So situation can be changed at any time.

@Vern Jensen 2015-09-01 22:09:37

Is it possible to change the template's HTML at run-time using JavaScript? The HTML isn't known in advance for me to be able to type it into the template... it is computed on the fly.

@alexpods 2015-09-02 22:23:51

AFAIK You can't change properties of component decorators. And this is not a good idea.

@JimTheDev 2015-09-03 15:13:04

So, imaging the scenario where you have json coming back from the server over time. You want to show a diff between two of these json responses with some html formatting injected for green or red based on if a line was added or removed. This is not a supported use case in Angular2 as far as you know since the template is dynamic?

@JimTheDev 2015-09-03 16:12:20

For my use-case I was able to just add a style tag into the returned html. It is not pretty but works for now.

@alexpods 2015-09-03 16:32:10

@JimTheDev I updated the example, adding dynamic creation of component (dirty hack for me). What about your question with diffs, I don't see something special in this case. You just need to create JsonDiff component (<json-diff json1="firstJson" json2="secondJson"/>) which must be responsible for rendering the diff between two jsons. Maybe you'll need to create some auxiliary components for recursion handling, but not something really special.

@alexpods 2015-09-03 17:47:41

@vern-jensen I've updated question with dynamic component creation.

@Vern Jensen 2015-09-04 02:13:44

Very interesting that that dynamic component bit works. I'll have to look into that further. Thanks!

@Alex Barannikov 2015-11-02 18:08:42

Can someone provide an example of data binding between parent component and child inserted using DynamicComponentLoader? There are a lot of examples on how to insert child, but no examples on how to bind some data to it.

@drew moore 2015-12-07 02:03:46

@AlexBarannikov -DynamicComponentLoader accepts a providers argument for this purpose.

@Alex Barannikov 2015-12-08 06:31:22

@drewmoore, I thought that providers is for DI that can provide some services into component's constructor. But what I need is databinding to properties, like I will appreciate if you point me to some example with databinding, especially with "two-way"

@sathishkumar 2016-01-24 01:23:41

(wasted 2 hour) , The solution is very simple, Please check stackoverflow.com/questions/31548311/…

@vitaly-t 2017-10-24 13:47:00

This answer should be scrapped, it is obsolete, doesn't work anymore, and is a bad approach all the way. This is the way to go: stackoverflow.com/questions/31548311/…

Related Questions

Sponsored Content

20 Answered Questions

[SOLVED] Angular HTML binding

17 Answered Questions

[SOLVED] What is the equivalent of ngShow and ngHide in Angular 2+?

31 Answered Questions

[SOLVED] Can't bind to 'ngModel' since it isn't a known property of 'input'

7 Answered Questions

[SOLVED] Equivalent of $compile in Angular 2

3 Answered Questions

[SOLVED] What is the difference between parentheses, brackets and asterisks in Angular2?

  • 2016-03-11 16:03:07
  • David Meza
  • 66258 View
  • 130 Score
  • 3 Answer
  • Tags:   angular

2 Answered Questions

[SOLVED] Compile angular component to HTML with bindings

2 Answered Questions

angular 2 equivalent of react renderToString

Sponsored Content