9

It's possible evaluate template from string in a variable?. I need place the string in the component instead of the expression, e.g.

template: "<div>{{ template_string }}</div>"

template_string contains: <b>{{ name }}</b>

and all should be evaluated to <div><b>My Name</b></div>

but I see <div>{{ template_string }}</div>

I need something like {{ template_string | eval }} or something else to evaluate the content of the variable on current context.

It's possible? I need something to use this approach because template_string can be changed when the component is used.

Edit1:

Angular Version: 4.0.3

E.g.

@Component({
  selector: 'product-item',
  template: `
    <div class="product">{{ template }}</div>`,
})
export class ProductItemComponent {
  @Input() name: string;
  @Input() price: number = 0;
  @Input() template: string = `{{ name }} <b>{{ price | currency }}</b>`;
}

Usage:

<product-item [name]="product.name" [price]="product.price"></product-item>

Expected: Product Name USD3.00

Output: {{ name }} <b>{{ price | currency }}</b>

4
  • You may look at this stack overflow question Commented May 19, 2017 at 20:00
  • does not work, tested with 4.0.3, output the string as is: <div>{{ template_string }}</div> Commented May 19, 2017 at 20:15
  • Are you using AOT? Commented May 19, 2017 at 21:10
  • not, suppose JIT, I'm currently running whit npm start. I'm a beginner, this is my first project using angular2. Commented May 19, 2017 at 22:21

3 Answers 3

19

You can create your own directive that will do it:

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;
  }
}

Usage:

@Component({
  selector: 'product-item',
  template: `
    <div class="product">
      <ng-container *compile="template; context: this"></ng-container>
    </div>
  `,
})
export class ProductItemComponent {
  @Input() name: string;
  @Input() price: number = 0;
  @Input() template: string = `{{ name }} <b>{{ price | currency }}</b>`;
}

Plunker Example

See also

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

3 Comments

Thanks!! again ;)
This solution seems to break if the compile directive is used more than once? Any components I declare in the declarations of the dynamic module will be loaded in two modules with the same name, and the compilation crashes.
0

not sure how you're building the template string

import { ..., OnInit } from '@angular/core';

@Component({
    selector: 'product-item',
    template: `
    <div class="product" [innerHtml]='template_string'>
    </div>`,
    })
export class ProductItemComponent implements OnInit {
    @Input() name: string;
    @Input() price: number = 0;
    @Input() pre: string;
    @Input() mid: string;
    @Input() post: string;
    template_string;
    ngOnInit() {
        // this is probably what you want
        this.template_string = `${this.pre}${this.name}${this.mid}${this.price}${this.post}`
    }
}

<product-item [name]="name" [price]="price" pre="<em>" mid="</em><b>" post="</b>"></product-item>

the string can be built from outside the component, would still recommend something like ngIf to control dynamic templates though.

1 Comment

it's not my use case, I really need evaluate from string, see: stackoverflow.com/q/44073940/4724040, it's a similar unresolved use case
0

In Angular double curly braces {{}} are used to evaluation an expression in a component's template. and not work on random strings or dynamically added DOM elements. So one way of doing this is to use typescript string interpolation using ${}. check the rest of code to understand

@Component({
  selector: 'product-item',
  template: `
    <div class="product" [innerHTML]="template"></div>`,
})
export class ProductItemComponent {
  @Input() name: string;
  @Input() price: number = 0;
  @Input() template: string = `${ this.name } <b>${ this.price }}</b>`;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.