Come posso usare / creare template dinamico per compilare componenti dinamici con Angular 2.0?

Voglio creare dynamicmente il modello . Questo dovrebbe essere usato per build un ComponentType in Runtime e posizionarlo (anche sostituirlo) da qualche parte all’interno del componente di hosting.

Fino a RC4 stavo usando ComponentResolver , ma con RC5 ricevo il messaggio:

ComponentResolver è deprecato per la compilazione dynamic. Utilizzare invece ComponentFactoryResolver con il @NgModule/@Component.entryComponents o ANALYZE_FOR_ENTRY_COMPONENTS. Solo per la compilazione del runtime , è ansible utilizzare anche Compiler.compileComponentSync/Async .

Ho trovato questo documento (offical angular2)

Creazione di componenti dinamici sincroni 2 angolari

E capisco che posso usare entrambi

  • Tipo di dynamic ngIf con ComponentFactoryResolver . Se passerò i componenti noti nell’hosting all’interno di @Component({entryComponents: [comp1, comp2], ...}) – Posso usare .resolveComponentFactory(componentToRender);
  • Compilazione real runtime, con Compiler

Ma la domanda è come usare quel Compiler ? La nota sopra dice che dovrei chiamare: Compiler.compileComponentSync/Async – quindi come?

Per esempio. Voglio creare (in base ad alcune condizioni di configurazione) questo tipo di modello per un tipo di impostazioni

 
...

e in un altro caso questo (l’ string-editor viene sostituito con l’ text-editor )

   ... 

E così via (diversi numeri / date / editors riferimento per tipi di proprietà, saltate alcune proprietà per alcuni utenti …) . Cioè questo è un esempio, la configurazione reale potrebbe generare modelli molto più diversi e complessi.

Il modello sta cambiando , quindi non posso usare ComponentFactoryResolver e passare quelli esistenti … Ho bisogno di una soluzione con il Compiler


AOT e JitCompiler (ex RuntimeCompiler)

Vorresti utilizzare queste funzionalità con AOT (compilazione anticipata)? Stai ottenendo:

Errore: errore rilevato durante la risoluzione statica dei valori dei simboli. Le chiamate di funzione non sono supportate. Considera di sostituire la funzione o lambda con un riferimento a una funzione esportata (posizione 65:17 nel file .ts originale), risolvendo il simbolo COMPILER_PROVIDERS in … / node_modules/@angular/compiler/src/compiler.d.ts,

Per favore, lascia il tuo commento, vota qui:

Potrebbe / vorrebbe / verrà codificato utilizzando COMPILER_PROVIDERS essere supportato da AOT?

EDIT – relativo a 2.3.0 (2016-12-07)

NOTA: per ottenere una soluzione per la versione precedente, controllare la cronologia di questo post

Argomento simile è discusso qui Equivalente di $ compile in Angular 2 . Dobbiamo usare JitCompiler e NgModule . Maggiori informazioni su NgModule in Angular2 qui:

  • Angular 2 RC5 – NgModules, Lazy Loading e compilazione AoT

In poche parole

C’è un plunker / esempio funzionante (modello dinamico, tipo di componente dinamico, modulo dinamico, JitCompiler , … in azione)

Il principale è:
1) crea il modello
2) trova ComponentFactory nella cache – vai a 7)
3): crea Component
4) – crea il Module
5) – Compila Module
6) – return (e cache per uso futuro) ComponentFactory
7) utilizzare Target e ComponentFactory per creare un’istanza di Component dynamic

Ecco uno snippet di codice (più qui ) – Il nostro Builder personalizzato sta restituendo solo ComponentFactory costruito / memorizzato nella cache e la vista Target placeholder consuma per creare un’istanza di 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; //... }); 

Questo è – in poche parole. Per ottenere maggiori dettagli leggi sotto

.

TL & DR

Osserva un plunker e torna a leggere i dettagli nel caso in cui alcuni frammenti richiedano ulteriori spiegazioni

.

Spiegazione dettagliata – Angular2 RC6 ++ e componenti di runtime

Di seguito la descrizione di questo scenario , lo faremo

  1. crea un modulo PartsModule:NgModule (titolare di piccoli pezzi)
  2. crea un altro modulo DynamicModule:NgModule , che conterrà il nostro componente dinamico (e di riferimento PartsModule modo dinamico)
  3. crea template dinamico (approccio semplice)
  4. crea un nuovo tipo di Component (solo se il modello è cambiato)
  5. crea un nuovo RuntimeModule:NgModule . Questo modulo conterrà il tipo di Component creato in precedenza
  6. chiama JitCompiler.compileModuleAndAllComponentsAsync(runtimeModule) per ottenere ComponentFactory
  7. crea un’istanza di DynamicComponent – lavoro del segnaposto Target View e ComponentFactory
  8. assegnare @Inputs a una nuova istanza (passare da INPUT a TEXTAREA editing) , utilizzare @Outputs

NgModule

Abbiamo bisogno di un NgModule s.

Mentre mi piacerebbe mostrare un esempio molto semplice, in questo caso, avrei bisogno di tre moduli (in realtà 4 – ma non conto AppModule) . Per favore, prendi questo piuttosto che un semplice frammento come base per un generatore di componenti dinamici davvero solido.

Ci sarà un modulo per tutti i piccoli componenti, ad es. 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 { } 

Dove DYNAMIC_DIRECTIVES sono estensibili e sono destinati a contenere tutte le parti piccole utilizzate per il nostro modello / tipo di componente dinamico. Controlla app / parti / parts.module.ts

Il secondo sarà il modulo per la nostra gestione delle cose dinamiche. Conterrà componenti di hosting e alcuni provider .. che saranno singleton. Perciò li pubblicheremo in modo standard – con 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 ], }; } } 

Verifica l’utilizzo di forRoot() in AppModule

Infine, avremo bisogno di un modulo ad hoc, runtime .. ma che verrà creato successivamente, come parte del lavoro di DynamicTypeBuilder .

Il quarto modulo, modulo di applicazione, è quello che continua a dichiarare i provider di compilatori:

 ... 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 ], 

Leggi (leggi) molto di più su NgModule lì:

  • Angular 2 RC5 – NgModules, Lazy Loading e compilazione AoT
  • Documentazione sui moduli angolari

Un costruttore di modelli

Nel nostro esempio elaboreremo i dettagli di questo tipo di entity framework

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

Per creare un template , in questo plunker usiamo questo semplice / ingenuo costruttore.

La vera soluzione, un vero costruttore di template, è il luogo in cui la tua applicazione può fare molto

 // 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 + ""; } } 

Un trucco qui: costruisce un modello che utilizza alcune serie di proprietà conosciute, ad esempio entity . Tale proprietà (-ies) deve far parte del componente dinamico, che verrà creato successivamente.

Per renderlo un po ‘più semplice, possiamo usare un’interfaccia per definire le proprietà che possono essere utilizzate dal nostro costruttore di template. Questo sarà implementato dal nostro tipo di componente dinamico.

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

Un builder ComponentFactory

La cosa molto importante qui è da tenere a mente:

il nostro tipo di componente, creato con DynamicTypeBuilder , potrebbe essere diverso, ma solo dal modello (creato sopra) . Le proprietà dei componenti (ingressi, uscite o alcune protette) sono sempre uguali. Se abbiamo bisogno di proprietà diverse, dovremmo definire una combinazione diversa di Template e Type Builder

Quindi, stiamo toccando il nucleo della nostra soluzione. The Builder, 1) crea ComponentType 2) crea il suo NgModule 3) compila ComponentFactory 4) nascondilo per riutilizzarlo in seguito.

Una dipendenza che dobbiamo ricevere:

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

Ed ecco uno snippet come ottenere una 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); }); }); } 

Sopra creiamo e mettiamo in cache sia il Component che il Module . Perché se il modello (in effetti la vera parte dynamic di tutto questo) è lo stesso … possiamo riutilizzare

E qui ci sono due metodi, che rappresentano il modo davvero interessante su come creare classi / tipi decorati in runtime. Non solo @Component ma anche @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; } 

Importante:

i nostri tipi dinamici di componenti differiscono, ma solo per modello. Quindi usiamo questo fatto per memorizzarli nella cache . Questo è davvero molto importante. Angular2 memorizzerà anche questi in cache .. per il tipo . E se dovessimo ricreare per lo stesso template stringhe di nuovi tipi … inizieremo a generare perdite di memoria.

ComponentFactory utilizzato dal componente di hosting

Il pezzo finale è un componente, che ospita l’objective per il nostro componente dinamico, ad esempio

. Otteniamo un riferimento e usiamo ComponentFactory per creare un componente. Questo è in poche parole, e qui ci sono tutti i pezzi di quel componente (se necessario, apri plunker qui )

In primo luogo, riassumiamo le dichiarazioni di importazione:

 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 ) {} ...

Riceviamo appena, costruttori di template e componenti. Poi ci sono le proprietà che sono necessarie per il nostro esempio (più nei commenti)

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

In questo semplice scenario, il nostro componente di hosting non ha alcun @Input . Quindi non deve reactjs ai cambiamenti. Ma a dispetto di questo fatto (e di essere pronto per i cambiamenti in arrivo) – dobbiamo introdurre qualche flag se il componente è già stato (inizialmente) avviato. E solo allora possiamo iniziare la magia.

Infine useremo il nostro componente builder e il suo ComponentFacotry compilato / memorizzato nella cache . Al nostro target placeholder verrà chiesto di creare un’istanza del Component con quella factory.

 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; //... }); } 

piccola estensione

Inoltre, dobbiamo mantenere un riferimento al modello compilato .. per poterlo destroy() correttamente destroy() , ogni volta che lo cambieremo.

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

fatto

Questo è praticamente tutto. Non dimenticare di distruggere tutto ciò che è stato creato dynamicmente (ngOnDestroy) . Inoltre, assicurati di memorizzare nella cache i types e i modules dinamici se l’unica differenza è il loro modello.

Controlla tutto in azione qui

per vedere le versioni precedenti (ad es. RC5 correlate) di questo post, controllare la cronologia

EDIT (26/08/2017) : La soluzione seguente funziona bene con Angular2 e 4. L’ho aggiornato per contenere una variabile di template e fare clic su handler e testato con Angular 4.3.
Per Angular4, ngComponentOutlet come descritto nella risposta di Ophir è una soluzione molto migliore. Ma al momento non supporta ancora input e output . Se [questa PR] ( https://github.com/angular/angular/pull/15362) è accettata, sarebbe ansible attraverso l’istanza del componente restituita dall’evento create.
ng-dynamic-component potrebbe essere la soluzione migliore e più semplice del tutto, ma non l’ho ancora testato.

La risposta di @Long Field è azzeccata! Ecco un altro esempio (sincrono):

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

In diretta su http://plnkr.co/edit/fdP9Oc .

Devo essere arrivato alla festa in ritardo, nessuna delle soluzioni qui mi è sembrata utile – troppo disordinata e sentita come una soluzione eccessiva.

Quello che ho finito è usare Angular 4.0.0-beta.6 beta.6 .

Questo mi ha dato la soluzione più breve e più semplice, tutto scritto nel file del componente dinamico.

  • Ecco un semplice esempio che riceve il testo e lo inserisce in un modello, ma ovviamente puoi cambiare in base alle tue necessità:
 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; } } 
  • Breve spiegazione:
    1. my-component : il componente in cui viene eseguito il rendering di un componente dinamico
    2. DynamicComponent : il componente da build in modo dinamico e viene visualizzato all’interno del mio componente

Non dimenticare di aggiornare tutte le librerie angolari a ^ Angular 4.0.0

Spero che questo aiuti, buona fortuna!

AGGIORNARE

Funziona anche per angular 5.

Ho deciso di compattare tutto ciò che ho imparato in un unico file . C’è molto da vedere qui soprattutto rispetto a prima di RC5. Si noti che questo file sorgente include AppModule e 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 {}`

Ho un semplice esempio per mostrare come fare il componente dinamico angular 2 rc6.

Supponiamo che tu abbia un template html dinamico = template1 e desideri caricare dynamicmente, prima di tutto passare al componente

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

qui template1 come html, può contenere componenti ng2

Da rc6, devi avere @NgModule per avvolgere questo componente. @NgModule, proprio come il modulo in anglarJS 1, disaccoppia diverse parti dell’applicazione ng2, quindi:

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

(Qui importa RouterModule come nel mio esempio ci sono alcuni componenti di route nel mio html come puoi vedere più avanti)

Ora puoi compilare DynamicModule come: this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then( factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))

E abbiamo bisogno di mettere sopra in app.moudule.ts per caricarlo, per favore vedi il mio app.moudle.ts. Per maggiori dettagli e maggiori dettagli: https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts e app.moudle.ts

e vedere la demo: http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p=preview

Voglio aggiungere qualche dettaglio in cima a questo post eccellente di Radim.

Ho preso questa soluzione e ci ho lavorato un po ‘e ho trovato alcune limitazioni. Li descriverò e poi darò la soluzione anche a questo.

  • Prima di tutto non ero in grado di eseguire il rendering di dettagli dinamici all’interno di un dettaglio dinamico (fondamentalmente nidi UI dinamici l’uno dentro l’altro).
  • Il prossimo numero era che volevo rendere un dettaglio dinamico all’interno di una delle parti che era stata resa disponibile nella soluzione. Neanche ciò è stato ansible con la soluzione iniziale.
  • Infine, non è stato ansible utilizzare gli URL dei modelli sulle parti dinamiche come l’editor delle stringhe.

Ho fatto un’altra domanda sulla base di questo post, su come raggiungere questi limiti, che possono essere trovati qui:

compilazione di template dinamici ricorsivi in ​​angular2

Indicherò semplicemente le risposte a queste limitazioni, se dovessi affrontare lo stesso problema di me, in quanto ciò rende la soluzione molto più flessibile. Sarebbe fantastico avere il plunker iniziale aggiornato anche con quello.

Per abilitare la nidificazione di dettagli dinamici l’uno nell’altro, è necessario aggiungere DynamicModule.forRoot () nell’istruzione import nel 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; } 

Inoltre non è stato ansible utilizzare all’interno di una delle parti come string-editor o text-editor.

Per triggersrlo dovrai cambiare parts.module.ts e dynamic.module.ts

All’interno di parts.module.ts Dovrai aggiungere DynamicDetail in DYNAMIC_DIRECTIVES

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

Hope this helps someone else!

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 .

Uso:

 

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

Spero che questo ti aiuti.

Saluti!