2

I am trying to bind data inside a div using [innerHtml]. How to notify angular to use the variables instead of the raw text. Here is my setup :

<div *ngFor="let data of someData">
    <div *ngFor="let odata of someOtherData;trackBy:id">
        <div [innerHTML]="odata.template | pipeToSanitize"></div>
    </div>
</div>

The data looks like this :

someOtherData = [{
                    'id': 1,
                    'template': '<div class="{{data.class}}"><md-icon>{{data.icon}}</md-icon></div>'
                }, 
                {
                    'id': 2,
                    'template': '<div>{{data.timestampStr}}</div>'
                }, 
                {
                    'id': 3,
                    'template': '<div>{{data.message}}</div>'
                }]

someData = [{
               'message': 'Message 1',
               'timestampStr': '2016/12/13 09:25:00',
               'class': 'events-warn-color',
               'icon': 'warning'
            }, 
           {
               'message': 'Message 2',
               'timestampStr': '2016/12/13 10:36:00',
               'class': 'events-warn-color',
               'icon': 'warning'
           }];

Now I am getting {{data.icon}},etc. as it is. How can replace this with the contents from 'someOtherData' object. Thanks in advance

4 Answers 4

3

You have to create compiler directive to evaluate the template string:

compile.directive.ts

  @Directive({
    selector: '[compile]'
  })
  export class CompileDirective implements OnChanges {
    @Input() compile: string;
    @Input() compileContext: any;

    compRef: ComponentRef<any>;

    constructor(private vcRef: ViewContainerRef, private compiler: Compiler) {}

    ngOnChanges() {
      if(!this.compile) {
        if(this.compRef) {
          this.updateProperties();
          return;
        }
        throw Error('You forgot to provide template');
      }

      this.vcRef.clear();
      this.compRef = null;

      const component = this.createDynamicComponent(this.compile);
      const module = this.createDynamicModule(component);
      this.compiler.compileModuleAndAllComponentsAsync(module)
        .then((moduleWithFactories: ModuleWithComponentFactories<any>) => {
          let compFactory = moduleWithFactories.componentFactories.find(x => x.componentType === component);

          this.compRef = this.vcRef.createComponent(compFactory);
          this.updateProperties();
        })
        .catch(error => {
          console.log(error);
        });
    }

    updateProperties() {
      for(var prop in this.compileContext) {
        this.compRef.instance[prop] = this.compileContext[prop];
      }
    }

    private createDynamicComponent (template:string) {
      @Component({
        selector: 'custom-dynamic-component',
        template: template,
      })
      class CustomDynamicComponent {}
      return CustomDynamicComponent;
    }

    private createDynamicModule (component: Type<any>) {
      @NgModule({
        // You might need other modules, providers, etc...
        // Note that whatever components you want to be able
        // to render dynamically must be known to this module
        imports: [CommonModule],
        declarations: [component]
      })
      class DynamicModule {}
      return DynamicModule;
    }
  }

Template

<div *ngFor="let data of someData">
    <div *ngFor="let odata of someOtherData;">
          <ng-container *compile="odata.template; context:{data:data}"></ng-container>
    </div>
</div>

You still need to deal with angular material module , I haven't impliment that to my online example, just check compile direactive comment

stackblitz template compile

Check this Angular2, evaluate template from string inside a component

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks bro. Works as expected. In AngularJS we could have done this with 2 lines.
0

Why use [innerHTML]?

Try

<div>{{odata.template | pipeToSanitize}}</div>

Comments

0

[innerHtml] is great option in most cases, but it fails with really large strings or when you need hard-coded styling in html.

you can check for answers in the following to get a good idea. Specially Piotrek's answer

Angular HTML binding

Comments

-1

EDITED: can you try this too:

<div *ngFor="let data of someData">
    <div *ngFor="let odata of getData(data);trackBy:id">
        <div [innerHTML]="odata.template | pipeToSanitize"></div>
    </div>
</div>

getDataFormated(data:any):any{
return [{
                    'id': 1,
                    'template': '<div class="{{data.class}}"><md-icon>{{data.icon}}</md-icon></div>'
                }, 
                {
                    'id': 2,
                    'template': '<div>{{data.timestampStr}}</div>'
                }, 
                {
                    'id': 3,
                    'template': '<div>{{data.message}}</div>'
                }]
}

4 Comments

Already tried that bro. The tslint is throwing error saying data is not defined.
Try the new solution guy!
Thanks for the reply. Changed the solution to use template literals and it works. But I will have to do extra 'dirty' work as the structure of 'someOtherData' can change.
There is md-icon component required runtime compile @DanielSeguraPérez

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.