Schede dinamiche angolari con componenti scelti dall’utente

Sto provando a configurare un sistema di tabs che permetta ai componenti di registrarsi (con un titolo). La prima scheda è come una casella di posta, ci sono un sacco di azioni / elementi di collegamento tra cui scegliere per gli utenti, e ognuno di questi clic dovrebbe essere in grado di creare un’istanza di un nuovo componente, al clic. Le azioni / collegamenti arrivano da JSON.

Il componente istanziato si registrerà quindi come una nuova scheda.

Non sono sicuro se questo è l’approccio “migliore”? Sofar le uniche guide che ho visto sono per tabs statiche, il che non aiuta.

Finora ho solo il servizio di tabs che è stato avviato in main per persistere in tutta l’app, sembra ~ qualcosa del genere.

export interface ITab { title: string; } @Injectable() export class TabsService { private tabs = new Set(); addTab(title: string): ITab { let tab: ITab = { title }; this.tabs.add(tab); return tab; } removeTab(tab: ITab) { this.tabs.delete(tab); } } 

Domande:

  1. Come posso avere un elenco dinamico nella Posta in arrivo che crea nuove (diverse) tabs? Sto indovinando che verrà utilizzato DynamicComponentBuilder ?
  2. In che modo i componenti creati dalla Posta in arrivo (al clic) si registrano come tabs e possono anche essere visualizzati? Sto indovinando ng-content , ma non riesco a trovare molte informazioni su come usarlo

Modifica: prova a chiarire

Pensa alla posta in arrivo come a una casella di posta, gli elementi vengono recuperati come JSON e visualizzano diversi elementi. Dopo aver fatto clic su uno degli elementi, viene creata una nuova scheda con tali elementi azione ‘tipo’. Il tipo è quindi un componente

Modifica2: immagine

http://sofit.miximages.com/angular-template/yzfMOXJ.png

aggiornare

Esempio 5 StackBlitz angular

aggiornare

ngComponentOutlet stato aggiunto a 4.0.0-beta.3

aggiornare

C’è un lavoro in corso NgComponentOutlet che fa qualcosa di simile https://github.com/angular/angular/pull/11235

RC.7

Plunker esempio RC.7

 // Helper component to add dynamic components @Component({ selector: 'dcl-wrapper', template: `
` }) export class DclWrapper { @ViewChild('target', {read: ViewContainerRef}) target: ViewContainerRef; @Input() type: Type; cmpRef: ComponentRef; private isViewInitialized:boolean = false; constructor(private componentFactoryResolver: ComponentFactoryResolver, private compiler: Compiler) {} updateComponent() { if(!this.isViewInitialized) { return; } if(this.cmpRef) { // when the `type` input changes we destroy a previously // created component before creating the new one this.cmpRef.destroy(); } let factory = this.componentFactoryResolver.resolveComponentFactory(this.type); this.cmpRef = this.target.createComponent(factory) // to access the created instance use // this.compRef.instance.someProperty = 'someValue'; // this.compRef.instance.someOutput.subscribe(val => doSomething()); } ngOnChanges() { this.updateComponent(); } ngAfterViewInit() { this.isViewInitialized = true; this.updateComponent(); } ngOnDestroy() { if(this.cmpRef) { this.cmpRef.destroy(); } } }

Esempio di utilizzo

 // Use dcl-wrapper component @Component({ selector: 'my-tabs', template: ` 

Tabs

` }) export class Tabs { @Input() tabs; }
 @Component({ selector: 'my-app', template: ` 

Hello {{name}}

` }) export class App { // The list of components to create tabs from types = [C3, C1, C2, C3, C3, C1, C1]; }
 @NgModule({ imports: [ BrowserModule ], declarations: [ App, DclWrapper, Tabs, C1, C2, C3], entryComponents: [C1, C2, C3], bootstrap: [ App ] }) export class AppModule {} 

Vedi anche angular.io CARICATORE COMPONENTE DINAMICO

versioni precedenti xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Questo è cambiato di nuovo in Angular2 RC.5

Aggiornerò l’esempio qui sotto ma è l’ultimo giorno prima delle vacanze.

Questo esempio di Plunker dimostra come creare dynamicmente i componenti in RC.5

Aggiornamento: utilizzare ViewContainerRef .createComponent ()

Poiché DynamicComponentLoader è deprecato, l’approccio deve essere aggiornato di nuovo.

 @Component({ selector: 'dcl-wrapper', template: `
` }) export class DclWrapper { @ViewChild('target', {read: ViewContainerRef}) target; @Input() type; cmpRef:ComponentRef; private isViewInitialized:boolean = false; constructor(private resolver: ComponentResolver) {} updateComponent() { if(!this.isViewInitialized) { return; } if(this.cmpRef) { this.cmpRef.destroy(); } this.resolver.resolveComponent(this.type).then((factory:ComponentFactory) => { this.cmpRef = this.target.createComponent(factory) // to access the created instance use // this.compRef.instance.someProperty = 'someValue'; // this.compRef.instance.someOutput.subscribe(val => doSomething()); }); } ngOnChanges() { this.updateComponent(); } ngAfterViewInit() { this.isViewInitialized = true; this.updateComponent(); } ngOnDestroy() { if(this.cmpRef) { this.cmpRef.destroy(); } } }

Esempio Plunker RC.4
Esempio plunker beta.17

Aggiorna: utilizza loadNextToLocation

 export class DclWrapper { @ViewChild('target', {read: ViewContainerRef}) target; @Input() type; cmpRef:ComponentRef; private isViewInitialized:boolean = false; constructor(private dcl:DynamicComponentLoader) {} updateComponent() { // should be executed every time `type` changes but not before `ngAfterViewInit()` was called // to have `target` initialized if(!this.isViewInitialized) { return; } if(this.cmpRef) { this.cmpRef.destroy(); } this.dcl.loadNextToLocation(this.type, this.target).then((cmpRef) => { this.cmpRef = cmpRef; }); } ngOnChanges() { this.updateComponent(); } ngAfterViewInit() { this.isViewInitialized = true; this.updateComponent(); } ngOnDestroy() { if(this.cmpRef) { this.cmpRef.destroy(); } } } 

Esempio plunker beta.17

originale

Non sono del tutto sicuro dalla tua domanda quali siano le tue esigenze ma penso che questo dovrebbe fare ciò che vuoi.

Il componente Tabs ottiene una serie di tipi passati e crea “tabs” per ogni elemento nell’array.

 @Component({ selector: 'dcl-wrapper', template: `
` }) export class DclWrapper { constructor(private elRef:ElementRef, private dcl:DynamicComponentLoader) {} @Input() type; ngOnChanges() { if(this.cmpRef) { this.cmpRef.dispose(); } this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => { this.cmpRef = cmpRef; }); } } @Component({ selector: 'c1', template: `

c1

` }) export class C1 { } @Component({ selector: 'c2', template: `

c2

` }) export class C2 { } @Component({ selector: 'c3', template: `

c3

` }) export class C3 { } @Component({ selector: 'my-tabs', directives: [DclWrapper], template: `

Tabs

` }) export class Tabs { @Input() tabs; } @Component({ selector: 'my-app', directives: [Tabs] template: `

Hello {{name}}

` }) export class App { types = [C3, C1, C2, C3, C3, C1, C1]; }

Esempio plunker beta.15 (non basato sul Plunker)

C’è anche un modo per passare i dati lungo che possono essere passati al componente creato dynamicmente come ( someData dovrebbero essere passati come type )

  this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => { cmpRef.instance.someProperty = someData; this.cmpRef = cmpRef; }); 

Vi è anche un certo supporto per utilizzare l’integrazione delle dipendenze con i servizi condivisi.

Per maggiori dettagli vedi https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html

Non sono abbastanza interessante per i commenti. Ho risolto il plunker dalla risposta accettata per lavorare con rc2. Niente di stravagante, i collegamenti al CDN sono stati appena infranti.

 '@angular/core': { main: 'bundles/core.umd.js', defaultExtension: 'js' }, '@angular/compiler': { main: 'bundles/compiler.umd.js', defaultExtension: 'js' }, '@angular/common': { main: 'bundles/common.umd.js', defaultExtension: 'js' }, '@angular/platform-browser-dynamic': { main: 'bundles/platform-browser-dynamic.umd.js', defaultExtension: 'js' }, '@angular/platform-browser': { main: 'bundles/platform-browser.umd.js', defaultExtension: 'js' }, 

https://plnkr.co/edit/kVJvI1vkzrLZJeRFsZuv?p=preview

c’è un componente pronto all’uso (compatibile con rc5) ng2-steps che usa il Compiler per iniettare il componente nel contenitore di fasi e il servizio per colbind tutto insieme (sincronizzazione dei dati)

  import { Directive , Input, OnInit, Compiler , ViewContainerRef } from '@angular/core'; import { StepsService } from './ng2-steps'; @Directive({ selector:'[ng2-step]' }) export class StepDirective implements OnInit{ @Input('content') content:any; @Input('index') index:string; public instance; constructor( private compiler:Compiler, private viewContainerRef:ViewContainerRef, private sds:StepsService ){} ngOnInit(){ //Magic! this.compiler.compileComponentAsync(this.content).then((cmpFactory)=>{ const injector = this.viewContainerRef.injector; this.viewContainerRef.createComponent(cmpFactory, 0, injector); }); } }