2016-06-29 11 views
21

C'è un modo per inserire dinamicamente un componente come figlio (non fratello) di un tag DOM in Angular 2?Angular2: inserire un componente dinamico come figlio di un contenitore nel DOM

Ci sono molti esempi lì intorno per inserire una componente dinamica come un fratello di un determinato tag ViewContainerRef s', come (a partire da RC3):

@Component({ 
    selector: '...', 
    template: '<div #placeholder></div>' 
}) 
export class SomeComponent { 
    @ViewChild('placeholder', {read: ViewContainerRef}) placeholder; 

    constructor(private componentResolver: ComponentResolver) {} 

    ngAfterViewInit() { 
    this.componentResolver.resolveComponent(MyDynamicComponent).then((factory) => { 
     this.componentRef = this.placeholder.createComponent(factory); 
    }); 
    } 
} 

Ma questo genera un DOM simile a:

<div></div> 
<my-dynamic-component></my-dynamic-component> 

risultato atteso:

<div> 
    <my-dynamic-component></my-dynamic-component> 
</div> 

Il ViewContainerRef diha lo stesso risultato, sta ancora inserendo il componente generato come fratello e non come figlio. Sarei a posto con una soluzione in cui il modello è vuoto e i componenti dinamici sono inseriti nel modello (all'interno del tag di selezione dei componenti).

La struttura DOM è molto importante quando si utilizzano librerie come ng2-dragula per trascinare da un elenco di componenti dinamici e benefit from the model updates. Il div extra è nella lista degli elementi trascinabili, ma al di fuori del modello, rompendo la resistenza di trascinamento &.

Alcuni dicono che non è possibile (c.f. this comment), ma suona come una limitazione molto sorprendente.

+1

Vedi https: // GitHub .com/angular/angular/issues/9035 –

+0

Grazie per la risposta rapida e il link utile! @pkozlowski ha dato la risposta alla mia domanda su questa discussione: usa '' invece di '

'. – Antoine

+0

Avete una soluzione che funzionerà anche su direttiva? poiché questo è per il componente – AngularOne

risposta

33

TL; DR: sostituire <div #placeholder></div> da <div><ng-template #placeholder></ng-template></div> inserire all'interno del div.

Ecco un working plunker (aggiornato per angolare 4), e il relativo codice:

@Component({ 
    selector: 'my-app', 
    template: `<div><ng-template #container></ng-template></div>` 
}) 
export class AppComponent implements OnInit { 

    @ViewChild('container', {read: ViewContainerRef}) viewContainer: ViewContainerRef; 

    constructor(private compiler: Compiler) {} 

    ngOnInit() { 
     this.createComponentFactory(MyDynamicComponent).then(
     (factory: ComponentFactory<MyDynamicComponent>) => this.viewContainer.createComponent(factory), 
     (err: any) => console.error(err)); 
    } 

    private createComponentFactory(/*...*/) {/*...*/} 

} 

Sembra <ng-container #placeholder></ng-container> sta anche lavorando (sostituire ng-template da ng-container). Mi piace questo approccio perché <ng-container> è chiaramente indirizzato a questo caso d'uso (un contenitore che non aggiunge un tag) e può essere utilizzato in altre situazioni come NgIf senza il wrapping in un tag reale.

PS: @ GünterZöchbauer mi ha indirizzato alla discussione giusta in un commento e alla fine ho risposto alla mia domanda.

+0

Il segnaposto non è noto, che è dinamico. Potrebbe essere possibile creare ViewChild dinamicamente? – Karthick

+0

@Karthick scusa per il ritardo. Credo che sia possibile ottenere un ViewChild completamente dinamico, ma non so come farlo. ViewChild ha diversi modi per identificare il componente da targetizzare. Potrebbe essere una domanda separata da chiedere :) – Antoine

1

Ero alla ricerca di una soluzione per lo stesso problema e la risposta approvata non ha funzionato per me. Ma ho trovato una soluzione migliore e più logica su come aggiungere un componente creato dinamicamente come elemento figlio all'elemento host corrente.

L'idea è di utilizzare il riferimento agli elementi del componente appena creato e aggiungerlo all'attuale elemento ref utilizzando il servizio di rendering. Possiamo ottenere l'oggetto componente tramite la sua proprietà injector.

Ecco il codice:

@Directive({ 
    selector: '[mydirective]' 
}) 
export class MyDirectiveDirective { 
    constructor(
     private cfResolver: ComponentFactoryResolver, 
     public vcRef: ViewContainerRef, 
     private renderer: Renderer2 
    ) {} 

    public appendComponent() { 
     const factory = 
     this.cfResolver.resolveComponentFactory(MyDynamicComponent); 
     const componentRef = this.vcRef.createComponent(factory); 
     this.renderer.appendChild(
      this.vcRef.element.nativeElement, 
      componentRef.injector.get(MyDynamicComponent).elRef.nativeElement 
     ); 
    } 
} 
0

mio modo sporco, basta salvare il riferimento del componente creata dinamicamente (fratello) e spostarlo con vaniglia JS:

constructor(
    private el: ElementRef, 
    private viewContainerRef: ViewContainerRef, 
    private componentFactoryResolver: ComponentFactoryResolver, 
) {} 

    ngOnInit() { 
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
     MyDynamicComponent, 
    ); 
    const componentRef = this.viewContainerRef.createComponent(componentFactory); 
    this.el.nativeElement.appendChild(componentRef.location.nativeElement); 
    } 
Problemi correlati