Angular2, оценить шаблон из строки внутри компонента

Можно ли оценить шаблон из строки в переменной ?. Мне нужно поместить строку в компонент вместо выражения, например

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

template_string содержит: <b>{{ name }}</b>

и все должно быть оценено как <div><b>My Name</b></div>

но я вижу <div>{{ template_string }}</div>

Мне нужно что-то вроде {{ template_string | eval }} или что-то еще, чтобы оценить содержимое переменной в текущем контексте.

Возможно? Мне нужно что-то, чтобы использовать этот подход, потому что template_string можно изменить при использовании компонента.

Edit1:

Угловая версия: 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>`;
}

Использование:

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

Ожидается: название продукта 3,00 доллара США

Вывод: {{ name }} <b>{{ price | currency }}</b>


person rafrsr    schedule 19.05.2017    source источник
comment
Вы можете посмотреть этот вопрос о переполнении стека   -  person Picci    schedule 19.05.2017
comment
не работает, проверено с 4.0.3, вывести строку как есть: <div>{{ template_string }}</div>   -  person rafrsr    schedule 19.05.2017
comment
Вы используете AOT?   -  person yurzui    schedule 20.05.2017
comment
нет, предположим, JIT, я сейчас работаю с npm start. Я новичок, это мой первый проект с использованием angular2.   -  person rafrsr    schedule 20.05.2017


Ответы (3)


Вы можете создать свою собственную директиву, которая будет делать это:

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

Использование:

@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

Смотрите также

person yurzui    schedule 20.05.2017
comment
Спасибо!! опять таки ;) - person rafrsr; 20.05.2017
comment
Кажется, это решение не работает, если директива компиляции используется более одного раза? Любые компоненты, которые я объявляю в объявлениях динамического модуля, будут загружены в два модуля с одинаковым именем, и компиляция выйдет из строя. - person zxz; 18.01.2018
comment
@zxz stackoverflow.com/questions/39678963/ - person yurzui; 18.01.2018

не уверен, как вы строите строку шаблона

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>

строка может быть построена извне компонента, тем не менее, порекомендовал бы что-то вроде ngIf для управления динамическими шаблонами.

person tesgo    schedule 19.05.2017
comment
это не мой вариант использования, мне действительно нужна оценка из строки, см. stackoverflow.com/q/44073940/4724040, это аналогичный нерешенный вариант использования - person rafrsr; 20.05.2017

В Angular двойные фигурные скобки {{}} используются для оценки выражения в шаблоне компонента. и не работают со случайными строками или динамически добавленными элементами DOM. Таким образом, один из способов сделать это - использовать интерполяцию строк машинописного текста с помощью ${}. проверьте остальной код, чтобы понять

@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>`;
}
person Khurram    schedule 20.05.2017