Как я могу использовать / создать динамический шаблон для компиляции динамического компонента с угловым 2.0?

Я хочу динамически создавать шаблон . Это должно использоваться для создания ComponentType во время выполнения и размещения (даже замены) его где-то внутри хостингового компонента.

До RC4 я использовал ComponentResolver , но с RC5 я получаю сообщение:

ComponentResolver устарел для динамической компиляции. Вместо этого используйте ComponentFactoryResolver вместе с @NgModule/@Component.entryComponents или ANALYZE_FOR_ENTRY_COMPONENTS. Для компиляции во время выполнения вы также можете использовать Compiler.compileComponentSync/Async .

Я нашел этот (оффициальный угловой) документ

Создание синхронного динамического компонента с угловым 2

И поймите, что я могу использовать либо

  • Вид динамического ngIf с ComponentFactoryResolver . Если я @Component({entryComponents: [comp1, comp2], ...}) известные компоненты в хостинг внутри @Component({entryComponents: [comp1, comp2], ...}) – я могу использовать .resolveComponentFactory(componentToRender);
  • Реальная компиляция времени выполнения с помощью Compiler

Но вопрос в том, как использовать этот Compiler ? В примечании выше говорится, что я должен позвонить: Compiler.compileComponentSync/Async – так как?

Например. Я хочу создать (на основе некоторых условий конфигурации) этот тип шаблона для одного вида настроек

 
...

и в другом случае этот ( string-editor заменяется text-editor )

   ... 

И так далее (разные editors номера / даты / ссылки по типам свойств, пропустили некоторые свойства для некоторых пользователей …) . Т.е. это пример, реальная конфигурация может генерировать гораздо более разные и сложные шаблоны.

Шаблон меняется , поэтому я не могу использовать ComponentFactoryResolver и передавать существующие … Мне нужно решение с помощью Compiler


AOT и JitCompiler (бывший RuntimeCompiler)

Вы хотели бы использовать эти функции с AOT (компиляция заранее)? Вы получаете:

Ошибка: ошибка обнаружила, что значения символа распознаются статически. Вызов функций не поддерживается. Рассмотрите возможность замены функции или lambda ссылкой на экспортированную функцию (позиция 65:17 в исходном файле .ts), разрешение символа COMPILER_PROVIDERS в … / node_modules/@angular/compiler/src/compiler.d.ts,

Пожалуйста, оставьте свой комментарий, проголосовате здесь:

Could / would / будет кодировать, используя COMPOLER_PROVIDERS, поддерживаемый AOT?

10 Solutions collect form web for “Как я могу использовать / создать динамический шаблон для компиляции динамического компонента с угловым 2.0?”

EDIT – относится к 2.3.0 (2016-12-07)

ПРИМЕЧАНИЕ. Чтобы получить решение для предыдущей версии, проверьте историю этого сообщения.

Аналогичная тема обсуждается здесь Эквивалент компиляции $ в Angular 2 . Нам нужно использовать JitCompiler и NgModule . Узнайте больше о NgModule в Angular2 здесь:

  • Угловые 2 RC5 – NgModules, ленивая загрузка и компиляция AoT

В двух словах

Существует рабочий плункер / пример (динамический шаблон, динамический тип компонента, динамический модуль, JitCompiler , … в действии)

Принцип:
1) создать шаблон
2) найдите ComponentFactory в кеше – перейдите к 7)
3) – создать Component
4) – создать Module
5) – компилировать Module
6) – возврат (и кеш для последующего использования) ComponentFactory
7) используйте Target и ComponentFactory для создания экземпляра динамического Component

Вот fragment кода (больше здесь ). Наш пользовательский Builder возвращает только что созданный / кэшированный ComponentFactory а вид Target placeholder потребляет для создания экземпляра DynamicComponent

  // here we get a TEMPLATE with dynamic content === TODO var template = this.templateBuilder.prepareTemplate(this.entity, useTextarea); // here we get Factory (just compiled or from cache) this.typeBuilder .createComponentFactory(template) .then((factory: ComponentFactory) => { // Target will instantiate and inject component (we'll keep reference to it) this.componentRef = this .dynamicComponentTarget .createComponent(factory); // let's inject @Inputs to component instance let component = this.componentRef.instance; component.entity = this.entity; //... }); 

Это все – в двух словах. Чтобы получить более подробную информацию, читайте ниже.

,

TL & DR

Наблюдайте за плунжером и возвращайтесь к чтению деталей, если какой-то fragment требует большего объяснения

,

Подробное объяснение – компоненты Angular2 RC6 ++ и времени выполнения

Ниже описания этого сценария мы будем

  1. создать модуль PartsModule:NgModule (держатель небольших кусков)
  2. создайте еще один модуль DynamicModule:NgModule , который будет содержать динамический компонент (и ссылочный PartsModule динамически)
  3. создать динамический шаблон (простой подход)
  4. создать новый тип Component (только если шаблон был изменен)
  5. создать новый RuntimeModule:NgModule . Этот модуль будет содержать ранее созданный тип Component
  6. вызовите JitCompiler.compileModuleAndAllComponentsAsync(runtimeModule) чтобы получить ComponentFactory
  7. создать экземпляр DynamicComponent – задание заполнителя View Target и ComponentFactory
  8. присвойте @Inputs новому экземпляру (переключитесь с редактирования INPUT на TEXTAREA ) , потребляйте @Outputs

NgModule

Нам нужен NgModule .

Хотя я хотел бы показать очень простой пример, в этом случае мне понадобятся три модуля (на самом деле 4 – но я не считаю AppModule) . Пожалуйста, возьмите это, а не простой fragment в качестве основы для действительно твердого динамического компонента-генератора.

Будет один модуль для всех небольших компонентов, например string-editor , text-editor ( text-editor date-editor number-editor …)

 @NgModule({ imports: [ CommonModule, FormsModule ], declarations: [ DYNAMIC_DIRECTIVES ], exports: [ DYNAMIC_DIRECTIVES, CommonModule, FormsModule ] }) export class PartsModule { } 

Где DYNAMIC_DIRECTIVES являются расширяемыми и предназначены для хранения всех мелких деталей, используемых для нашего динамического шаблона / типа компонента. Проверьте приложение / parts / parts.module.ts

Второй будет модулем для обработки данных Dynamic. Он будет содержать компоненты хостинга и некоторые провайдеры. Это будут синглеты. Поэтому мы опубликуем их стандартным способом – с forRoot()

 import { DynamicDetail } from './detail.view'; import { DynamicTypeBuilder } from './type.builder'; import { DynamicTemplateBuilder } from './template.builder'; @NgModule({ imports: [ PartsModule ], declarations: [ DynamicDetail ], exports: [ DynamicDetail], }) export class DynamicModule { static forRoot() { return { ngModule: DynamicModule, providers: [ // singletons accross the whole app DynamicTemplateBuilder, DynamicTypeBuilder ], }; } } 

Проверьте использование forRoot() в AppModule

Наконец, нам понадобится adhoc, runtime module .. но это будет создано позже, как часть задания DynamicTypeBuilder .

Четвертым модулем, модулем приложения является тот, кто продолжает объявлять компиляторы:

 ... import { COMPILER_PROVIDERS } from '@angular/compiler'; import { AppComponent } from './app.component'; import { DynamicModule } from './dynamic/dynamic.module'; @NgModule({ imports: [ BrowserModule, DynamicModule.forRoot() // singletons ], declarations: [ AppComponent], providers: [ COMPILER_PROVIDERS // this is an app singleton declaration ], 

Читайте (читайте) гораздо больше о NgModule :

  • Угловые 2 RC5 – NgModules, ленивая загрузка и компиляция AoT
  • Документация по угловым модулям

Создатель шаблонов

В нашем примере мы обработаем детали этого типа сущности

 entity = { code: "ABC123", description: "A description of this Entity" }; 

Чтобы создать template , в этом plunker мы используем этот простой / наивный строитель.

Реальное решение, настоящий шаблонный строитель, – это то место, где ваше приложение может многое сделать

 // plunker - app/dynamic/template.builder.ts import {Injectable} from "@angular/core"; @Injectable() export class DynamicTemplateBuilder { public prepareTemplate(entity: any, useTextarea: boolean){ let properties = Object.keys(entity); let template = ""; let editorName = useTextarea ? "text-editor" : "string-editor"; properties.forEach((propertyName) =>{ template += ` < ${editorName} [propertyName]="'${propertyName}'" [entity]="entity" >`; }); return template + ""; } } 

Трюк здесь – он создает шаблон, который использует некоторый набор известных свойств, например entity . Такое свойство (-ies) должно быть частью динамического компонента, который мы создадим дальше.

Чтобы сделать это немного проще, мы можем использовать интерфейс для определения свойств, которые может использовать наш построитель шаблонов. Это будет реализовано с помощью нашего динамического типа Component.

 export interface IHaveDynamicData { public entity: any; ... } 

ComponentFactory

Очень важно здесь иметь в виду:

наш тип компонента, построенный с помощью нашего DynamicTypeBuilder , может отличаться – но только по его шаблону (созданному выше) . Свойства компонентов (входы, выходы или некоторые защищенные) все те же. Если нам нужны разные свойства, мы должны определить различную комбинацию Template и Type Builder

Итак, мы трогаем суть нашего решения. Builder, будет: 1) создавать ComponentType 2) создавать свой NgModule 3) компилировать ComponentFactory 4) кэшировать его для последующего повторного использования.

Зависимость, которую мы должны получить:

 // plunker - app/dynamic/type.builder.ts import { JitCompiler } from '@angular/compiler'; @Injectable() export class DynamicTypeBuilder { // wee need Dynamic component builder constructor( protected compiler: JitCompiler ) {} 

И вот fragment, как получить ComponentFactory :

 // plunker - app/dynamic/type.builder.ts // this object is singleton - so we can use this as a cache private _cacheOfFactories: {[templateKey: string]: ComponentFactory} = {}; public createComponentFactory(template: string) : Promise> { let factory = this._cacheOfFactories[template]; if (factory) { console.log("Module and Type are returned from cache") return new Promise((resolve) => { resolve(factory); }); } // unknown template ... let's create a Type for it let type = this.createNewComponent(template); let module = this.createComponentModule(type); return new Promise((resolve) => { this.compiler .compileModuleAndAllComponentsAsync(module) .then((moduleWithFactories) => { factory = _.find(moduleWithFactories.componentFactories , { componentType: type }); this._cacheOfFactories[template] = factory; resolve(factory); }); }); } 

Выше мы создаем и кэшируем как Component и Module . Потому что, если шаблон (фактически реальная динамическая часть всего этого) тот же .. мы можем повторно использовать

И вот два метода, которые представляют собой действительно classный способ создания украшенных classов / типов во время выполнения. Не только @Component но и @NgModule

 protected createNewComponent (tmpl:string) { @Component({ selector: 'dynamic-component', template: tmpl, }) class CustomDynamicComponent implements IHaveDynamicData { @Input() public entity: any; }; // a component for this particular template return CustomDynamicComponent; } protected createComponentModule (componentType: any) { @NgModule({ imports: [ PartsModule, // there are 'text-editor', 'string-editor'... ], declarations: [ componentType ], }) class RuntimeComponentModule { } // a module for just this Type return RuntimeComponentModule; } 

Важный:

наши динамические типы компонентов различаются, но только по шаблону. Поэтому мы используем этот факт для их кэширования . Это действительно очень важно. Angular2 также будет кэшировать эти .. по типу . И если мы воссоздаем для тех же шаблонов строки новые типы …, мы начнем генерировать утечки памяти.

ComponentFactory используемый компонентом хостинга

Заключительная часть – это компонент, в котором размещается цель для нашего динамического компонента, например

. Мы получаем ссылку на него и используем ComponentFactory для создания компонента. Это в двух словах, и вот все части этого компонента (при необходимости, открытый плункер здесь )

Давайте сначала обобщим импортные заявления:

 import {Component, ComponentRef,ViewChild,ViewContainerRef} from '@angular/core'; import {AfterViewInit,OnInit,OnDestroy,OnChanges,SimpleChange} from '@angular/core'; import { IHaveDynamicData, DynamicTypeBuilder } from './type.builder'; import { DynamicTemplateBuilder } from './template.builder'; @Component({ selector: 'dynamic-detail', template: ` 
check/uncheck to use INPUT vs TEXTAREA:

entity:
{{entity | json}}

`, }) export class DynamicDetail implements AfterViewInit, OnChanges, OnDestroy, OnInit { // wee need Dynamic component builder constructor( protected typeBuilder: DynamicTypeBuilder, protected templateBuilder: DynamicTemplateBuilder ) {} ...

Мы просто получаем, шаблоны и компоновщики компонентов. Далее следуют свойства, которые необходимы для нашего примера (подробнее в комментариях)

 // reference for a 
with #dynamicContentPlaceHolder @ViewChild('dynamicContentPlaceHolder', {read: ViewContainerRef}) protected dynamicComponentTarget: ViewContainerRef; // this will be reference to dynamic content - to be able to destroy it protected componentRef: ComponentRef; // until ngAfterViewInit, we cannot start (firstly) to process dynamic stuff protected wasViewInitialized = false; // example entity ... to be recieved from other app parts // this is kind of candiate for @Input protected entity = { code: "ABC123", description: "A description of this Entity" };

В этом простом сценарии наш хостинг-компонент не имеет @Input . Поэтому он не должен реагировать на изменения. Но, несмотря на этот факт (и чтобы быть готовым к предстоящим изменениям), нам нужно ввести некоторый флаг, если компонент уже (поначалу) был инициирован. И только тогда мы сможем начать магию.

Наконец, мы будем использовать наш компонентный компоновщик и его только что скомпилированный / кэшированный ComponentFacotry . У нашего целевого заполнителя будет предложено создать экземпляр Component с этой фабрикой.

 protected refreshContent(useTextarea: boolean = false){ if (this.componentRef) { this.componentRef.destroy(); } // here we get a TEMPLATE with dynamic content === TODO var template = this.templateBuilder.prepareTemplate(this.entity, useTextarea); // here we get Factory (just compiled or from cache) this.typeBuilder .createComponentFactory(template) .then((factory: ComponentFactory) => { // Target will instantiate and inject component (we'll keep reference to it) this.componentRef = this .dynamicComponentTarget .createComponent(factory); // let's inject @Inputs to component instance let component = this.componentRef.instance; component.entity = this.entity; //... }); } 

небольшое расширение

Кроме того, нам нужно сохранить ссылку на скомпилированный шаблон .., чтобы иметь возможность правильно destroy() , когда мы его изменим.

 // this is the best moment where to start to process dynamic stuff public ngAfterViewInit(): void { this.wasViewInitialized = true; this.refreshContent(); } // wasViewInitialized is an IMPORTANT switch // when this component would have its own changing @Input() // - then we have to wait till view is intialized - first OnChange is too soon public ngOnChanges(changes: {[key: string]: SimpleChange}): void { if (this.wasViewInitialized) { return; } this.refreshContent(); } public ngOnDestroy(){ if (this.componentRef) { this.componentRef.destroy(); this.componentRef = null; } } 

сделанный

Это в значительной степени. Не забудьте уничтожить все, что было построено динамически (ngOnDestroy) . Кроме того, не забудьте кэшировать динамические types и modules если единственным отличием является их шаблон.

Проверьте все это в действии здесь

чтобы увидеть предыдущие версии (например, связанные с RC5) этого сообщения, проверьте историю

EDIT (26/08/2017) : Решение ниже хорошо работает с Angular2 и 4. Я обновил его, чтобы он содержал переменную шаблона и обработчик кликов и протестировал его с помощью Angular 4.3.
Для Angular4 ngComponentOutlet, как описано в ответе Офира, является гораздо лучшим решением. Но сейчас он не поддерживает входы и выходы . Если [этот PR] ( https://github.com/angular/angular/pull/15362) принят, это возможно через экземпляр компонента, возвращаемый событием create.
ng-dynamic-component может быть лучшим и самым простым решением, но я еще не тестировал это.

Ответ @Long Field – это место! Вот еще один (синхронный) пример:

 import {Compiler, Component, NgModule, OnInit, ViewChild, ViewContainerRef} from '@angular/core' import {BrowserModule} from '@angular/platform-browser' @Component({ selector: 'my-app', template: `

Dynamic template:

` }) export class App implements OnInit { @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef; constructor(private compiler: Compiler) {} ngOnInit() { this.addComponent( `

Click to increase: {{counter}} `enter code here`

`, { counter: 1, increaseCounter: function () { this.counter++; } } ); } private addComponent(template: string, properties?: any = {}) { @Component({template}) class TemplateComponent {} @NgModule({declarations: [TemplateComponent]}) class TemplateModule {} const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule); const factory = mod.componentFactories.find((comp) => comp.componentType === TemplateComponent ); const component = this.container.createComponent(factory); Object.assign(component.instance, properties); // If properties are changed at a later stage, the change detection // may need to be triggered manually: // component.changeDetectorRef.detectChanges(); } } @NgModule({ imports: [ BrowserModule ], declarations: [ App ], bootstrap: [ App ] }) export class AppModule {}

Живите по адресу http://plnkr.co/edit/fdP9Oc .

Я, должно быть, приехал на вечеринку поздно, ни одно из решений здесь не показалось мне полезным – слишком беспорядочно и было похоже на слишком много обходного пути.

То, что я закончил, – это использование Angular 4.0.0-beta.6 beta.6 .

Это дало мне самое кратчайшее, самое простое решение, написанное в файле динамического компонента.

  • Вот простой пример, который просто получает текст и помещает его в шаблон, но, очевидно, вы можете изменить в соответствии с вашими потребностями:
 import { Component, OnInit, Input, NgModule, NgModuleFactory, Compiler } from '@angular/core'; @Component({ selector: 'my-component', template: ``, styleUrls: ['my.component.css'] }) export class MyComponent implements OnInit { dynamicComponent; dynamicModule: NgModuleFactory; @Input() text: string; constructor(private compiler: Compiler) { } ngOnInit() { this.dynamicComponent = this.createNewComponent(this.text); this.dynamicModule = this.compiler.compileModuleSync(this.createComponentModule(this.dynamicComponent)); } protected createComponentModule (componentType: any) { @NgModule({ imports: [], declarations: [ componentType ], entryComponents: [componentType] }) class RuntimeComponentModule { } // a module for just this Type return RuntimeComponentModule; } protected createNewComponent (text:string) { let template = `dynamically created template with text: ${text}`; @Component({ selector: 'dynamic-component', template: template }) class DynamicComponent implements OnInit{ text: any; ngOnInit() { this.text = text; } } return DynamicComponent; } } 
  • Краткое объяснение:
    1. my-component – компонент, в котором выполняется динамический компонент
    2. DynamicComponent – компонент, который будет динамически построен, и он выполняет рендеринг внутри моего компонента

Не забудьте обновить все угловые библиотеки до ^ Angular 4.0.0

Надеюсь это поможет. Удачи!

ОБНОВИТЬ

Также работает для углового 5.

Я решил сжать все, что я узнал, в один файл . Здесь очень много, особенно для RC5. Обратите внимание, что этот исходный файл содержит AppModule и AppComponent.

 import { Component, Input, ReflectiveInjector, ViewContainerRef, Compiler, NgModule, ModuleWithComponentFactories, OnInit, ViewChild } from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; @Component({ selector: 'app-dynamic', template: '

Dynamic Components


' }) export class DynamicComponentRenderer implements OnInit { factory: ModuleWithComponentFactories; constructor(private vcRef: ViewContainerRef, private compiler: Compiler) { } ngOnInit() { if (!this.factory) { const dynamicComponents = { sayName1: {comp: SayNameComponent, inputs: {name: 'Andrew Wiles'}}, sayAge1: {comp: SayAgeComponent, inputs: {age: 30}}, sayName2: {comp: SayNameComponent, inputs: {name: 'Richard Taylor'}}, sayAge2: {comp: SayAgeComponent, inputs: {age: 25}}}; this.compiler.compileModuleAndAllComponentsAsync(DynamicModule) .then((moduleWithComponentFactories: ModuleWithComponentFactories) => { this.factory = moduleWithComponentFactories; Object.keys(dynamicComponents).forEach(k => { this.add(dynamicComponents[k]); }) }); } } addNewName(value: string) { this.add({comp: SayNameComponent, inputs: {name: value}}) } addNewAge(value: number) { this.add({comp: SayAgeComponent, inputs: {age: value}}) } add(comp: any) { const compFactory = this.factory.componentFactories.find(x => x.componentType === comp.comp); // If we don't want to hold a reference to the component type, we can also say: const compFactory = this.factory.componentFactories.find(x => x.selector === 'my-component-selector'); const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); const cmpRef = this.vcRef.createComponent(compFactory, this.vcRef.length, injector, []); Object.keys(comp.inputs).forEach(i => cmpRef.instance[i] = comp.inputs[i]); } } @Component({ selector: 'app-age', template: '
My age is {{age}}!
' }) class SayAgeComponent { @Input() public age: number; }; @Component({ selector: 'app-name', template: '
My name is {{name}}!
' }) class SayNameComponent { @Input() public name: string; }; @NgModule({ imports: [BrowserModule], declarations: [SayAgeComponent, SayNameComponent] }) class DynamicModule {} @Component({ selector: 'app-root', template: `

{{message}}



`, }) export class AppComponent { message = 'this is app component'; @ViewChild(DynamicComponentRenderer) dcr; } @NgModule({ imports: [BrowserModule], declarations: [AppComponent, DynamicComponentRenderer], bootstrap: [AppComponent] }) export class AppModule {}`

У меня есть простой пример, чтобы показать, как сделать угловой 2 динамический компонент rc6.

Скажем, у вас есть динамический шаблон html = template1 и вы хотите динамическую загрузку, сначала оберните компонент

 @Component({template: template1}) class DynamicComponent {} 

здесь template1 как html, может содержать компонент ng2

Из rc6 необходимо, чтобы @NgModule обернул этот компонент. @NgModule, как и модуль в anglarJS 1, он отделяет разную часть приложения ng2, поэтому:

 @Component({ template: template1, }) class DynamicComponent { } @NgModule({ imports: [BrowserModule,RouterModule], declarations: [DynamicComponent] }) class DynamicModule { } 

(Здесь import RouterModule, так как в моем примере в моем html есть некоторые компоненты маршрута, как вы можете видеть позже)

Теперь вы можете скомпилировать DynamicModule как: this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then( factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))

И мы должны поставить выше в app.moudule.ts, чтобы загрузить его, см. Мой app.moudle.ts. Для получения дополнительной и полной информации проверьте: https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts и app.moudle.ts

и посмотреть демо: http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p=preview

Я хочу добавить несколько деталей поверх этой замечательной публикации от Radim.

Я принял это решение и немного поработал над ним и быстро столкнулся с некоторыми ограничениями. Я просто опишу их, а затем даю решение этому.

  • Прежде всего, мне не удалось выполнить динамическую детализацию внутри динамической детали (в основном динамические пользовательские интерфейсы гнезд внутри друг друга).
  • Следующий вопрос состоял в том, что я хотел сделать динамическую деталь внутри одной из частей, которые были доступны в решении. Это было невозможно с начальным решением.
  • Наконец, не удалось использовать шаблонные URL-адреса в динамических частях, таких как строковый редактор.

Я задал другой вопрос, основанный на этом сообщении, о том, как достичь этих ограничений, которые можно найти здесь:

рекурсивная динамическая компиляция шаблона в угловом2

Я просто опишу ответы на эти ограничения, если вы столкнетесь с той же проблемой, что и я, поскольку это делает решение более гибким. Было бы здорово, если бы начальный плункер также был обновлен.

Чтобы включить динамическую детализацию внутри друг друга, вам нужно добавить DynamicModule.forRoot () в инструкции import в файле type.builder.ts

 protected createComponentModule (componentType: any) { @NgModule({ imports: [ PartsModule, DynamicModule.forRoot() //this line here ], declarations: [ componentType ], }) class RuntimeComponentModule { } // a module for just this Type return RuntimeComponentModule; } 

Кроме того, невозможно было использовать внутри одной из частей, являющейся редактором строк или текстовым редактором.

Чтобы включить это, вам нужно будет изменить parts.module.ts и dynamic.module.ts

Внутри parts.module.ts Вам необходимо добавить DynamicDetail в DynamicDetail

 export const DYNAMIC_DIRECTIVES = [ forwardRef(() => StringEditor), forwardRef(() => TextEditor), DynamicDetail ]; 

Also in the dynamic.module.ts you’d have to remove the dynamicDetail as they are now part of the parts

 @NgModule({ imports: [ PartsModule ], exports: [ PartsModule], }) 

A working modified plunker can be found here: http://plnkr.co/edit/UYnQHF?p=preview (I didn’t solve this issue, I’m just the messenger :-D)

Finally it was not possible to use templateurls in the parts created on the dynamic components. A solution (or workaround. I’m not sure whether it’s an angular bug or wrong use of the framework) was to create a compiler in the constructor instead of injecting it.

  private _compiler; constructor(protected compiler: RuntimeCompiler) { const compilerFactory : CompilerFactory = platformBrowserDynamic().injector.get(CompilerFactory); this._compiler = compilerFactory.createCompiler([]); } 

Then use the _compiler to compile, then templateUrls are enabled as well.

 return new Promise((resolve) => { this._compiler .compileModuleAndAllComponentsAsync(module) .then((moduleWithFactories) => { let _ = window["_"]; factory = _.find(moduleWithFactories.componentFactories, { componentType: type }); this._cacheOfFactories[template] = factory; resolve(factory); }); }); 

Надеюсь, это поможет кому-то еще!

Best regards Morten

Following up on Radmin’s excellent answer, there is a little tweak needed for everyone who is using angular-cli version 1.0.0-beta.22 and above.

COMPILER_PROVIDERS can no longer be imported (for details see angular-cli GitHub ).

So the workaround there is to not use COMPILER_PROVIDERS and JitCompiler in the providers section at all, but use JitCompilerFactory from ‘@angular/compiler’ instead like this inside the type builder class:

 private compiler: Compiler = new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler(); 

As you can see, it is not injectable and thus has no dependencies with the DI. This solution should also work for projects not using angular-cli.

Solved this in Angular 2 Final version simply by using the dynamicComponent directive from ng-dynamic .

Применение:

 

Where template is your dynamic template and context can be set to any dynamic datamodel that you want your template to bind to.

I myself am trying to see how can I update RC4 to RC5 and thus I stumbled upon this entry and new approach to dynamic component creation still holds a bit of mystery to me, so I wont suggest anything on component factory resolver.

But, what I can suggest is a bit clearer approach to component creation on this scenario – just use switch in template that would create string editor or text editor according to some condition, like this:

 

And by the way, “[” in [prop] expression have a meaning, this indicates one way data binding, hence you can and even should omit those in case if you know that you do not need to bind property to variable.

Building on top of the answer by Ophir Stern, here is a variant which works with AoT in Angular 4. The only issue I have is I cant inject any services into the DynamicComponent, but I can live with that.

note: I haven’t tested with Angular 5.

 import { Component, OnInit, Input, NgModule, NgModuleFactory, Compiler, EventEmitter, Output } from '@angular/core'; import { JitCompilerFactory } from '@angular/compiler'; export function createJitCompiler() { return new JitCompilerFactory([{ useDebug: false, useJit: true }]).createCompiler(); } type Bindings = { [key: string]: any; }; @Component({ selector: 'app-compile', template: ` 
`, styleUrls: ['./compile.component.scss'], providers: [{provide: Compiler, useFactory: createJitCompiler}] }) export class CompileComponent implements OnInit { public dynamicComponent: any; public dynamicModule: NgModuleFactory; @Input() public bindings: Bindings = {}; @Input() public template: string = ''; constructor(private compiler: Compiler) { } public ngOnInit() { try { this.loadDynamicContent(); } catch (err) { console.log('Error during template parsing: ', err); } } private loadDynamicContent(): void { this.dynamicComponent = this.createNewComponent(this.template, this.bindings); this.dynamicModule = this.compiler.compileModuleSync(this.createComponentModule(this.dynamicComponent)); } private createComponentModule(componentType: any): any { const runtimeComponentModule = NgModule({ imports: [], declarations: [ componentType ], entryComponents: [componentType] })(class RuntimeComponentModule { }); return runtimeComponentModule; } private createNewComponent(template: string, bindings: Bindings): any { const dynamicComponent = Component({ selector: 'app-dynamic-component', template: template })(class DynamicComponent implements OnInit { public bindings: Bindings; constructor() { } public ngOnInit() { this.bindings = bindings; } }); return dynamicComponent; } }

Надеюсь это поможет.

Ура!

  • BehaviorSubject vs Observable?
  • Angular2 + http с интервалом
  • Angular / RxJs Когда я должен отказаться от подписки на `Subscription`
  • Угловой 2 Используйте компонент из другого модуля
  • ключ доступа и значение объекта с помощью * ngFor
  • Как динамически загружать внешние скрипты в Angular?
  • Импорт lodash в приложение углового2 +
  • Перезагрузка текущего состояния - обновление данных
  • Как связать Http-вызовы в Angular2?
  • Внедрение $ scope в функцию угловой службы ()
  • Как использовать фильтр в controllerе?
  • Interesting Posts

    node.js require () cache – можно сделать недействительным?

    Преобразование даты / времени для данного часового пояса – java

    Зачем стирать файл со случайными шаблонами вместо всех 0 или 1?

    Как работает Intel Turbo Boost?

    Как вы можете автоматически запускать скрипт после подключения к VPN на OS X?

    Самый простой способ установить изображение в качестве фона JPanel

    Разница между конструктором и ngOnInit

    Почему Windows думает, что моя беспроводная клавиатура является тостером?

    ASP.NET MVC – привязка объекта типа «MODELNAME» не удалась, поскольку другой объект того же типа уже имеет такое же значение первичного ключа

    jQuery отключить параметры SELECT на основе выбранного радио (нужна поддержка для всех браузеров)

    Регулярное выражение для сопоставления чисел с запятыми или десятичными знаками в тексте

    Не должен ли ускоряться эмулятор Android при работе на ПК на базе Linux и ARM?

    MySQL: выберите все даты в диапазоне, даже если записей нет

    Как выделить синтаксис через Less

    Перетащите файл в приложение под управлением администратора

    Давайте будем гением компьютера.