2016-06-29 10 views
21

Czy istnieje sposób dynamicznego wstawiania komponentu jako dziecko (a nie jako rodzeństwo) znacznika DOM w Angular 2?Angular2: Wstaw komponent dynamiczny jako element potomny kontenera w DOM

Istnieje wiele przykładów wokół tam wstawić dynamiczny składnik jako rodzeństwo danego tagu ViewContainerRef „s, jak (od 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); 
    }); 
    } 
} 

Ale to generuje DOM podobny do:

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

Oczekiwany wynik:

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

Przy użyciu tego samego wyniku, ViewContainerRef, nadal wstawia wygenerowany komponent jako siostrzeniec, a nie dziecko. Byłbym w porządku z rozwiązaniem, w którym szablon jest pusty, a komponenty dynamiczne są wstawiane do szablonu (w znaczniku wyboru komponentu).

Struktura DOM jest bardzo ważna podczas korzystania z bibliotek takich jak ng2-dragula do przeciągania z listy dynamicznych komponentów i benefit from the model updates. Dodatkowy div znajduje się na liście elementów przeciągalnych, ale poza modelem, łamiąc logikę kropli &.

Niektórzy mówią, że nie jest to możliwe (c.f. this comment), ale brzmi to jak bardzo zaskakujące ograniczenie.

+1

Zobacz https: // github .pl/angle/angle/issues/9035 –

+0

Dzięki za szybką odpowiedź i użyteczny link! @pkozlowski udzielił odpowiedzi na moje pytanie w tej dyskusji: użyj '' zamiast '

'. – Antoine

+0

Czy masz rozwiązanie, które będzie działać również w sprawie dyrektywy? ponieważ jest to komponent – AngularOne

Odpowiedz

33

TL; DR zamienić <div #placeholder></div> przez <div><ng-template #placeholder></ng-template></div> wstawić wewnątrz div.

Oto working plunker (zaktualizowane do Kątowymi 4) i odpowiedni kod:

@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(/*...*/) {/*...*/} 

} 

Wydaje <ng-container #placeholder></ng-container> pracuje również (zastąpić ng-template przez ng-container). Podoba mi się to podejście, ponieważ <ng-container> jest wyraźnie adresowany do tego zastosowania (kontener, który nie dodaje znacznika) i może być używany w innych sytuacjach, takich jak NgIf bez zawijania w rzeczywistym tagu.

PS: @ GünterZöchbauer skierował mnie do właściwej dyskusji w komentarzu, a ja w końcu odpowiedziałem na moje własne pytanie.

+0

Symbol zastępczy nie jest znany, który jest dynamiczny. Czy możliwe jest dynamiczne tworzenie ViewChild? – Karthick

+0

@Karthick przepraszam za opóźnienie. Uważam, że możliwe jest uzyskanie całkowicie dynamicznej ViewChild, ale nie wiem jak to zrobić. ViewChild ma różne sposoby identyfikacji elementu, który ma być celem. Może to być osobne pytanie do zadania :) – Antoine

1

Szukałem rozwiązania dla tego samego problemu, a zatwierdzona odpowiedź nie działa dla mnie. Ale znalazłem lepsze i logiczne rozwiązanie, jak dołączyć dynamicznie utworzony komponent jako element potomny do bieżącego elementu hosta.

Ideą jest użycie odniesienia do elementu nowo utworzonego komponentu i dołączenie go do bieżącego ref elementu za pomocą usługi renderowania. Możemy uzyskać obiekt komponentu poprzez jego właściwość wtryskiwacza.

Oto kod:

@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

Moja brudny sposób, po prostu zachować odniesienie dynamicznie utworzonego składnika (rodzeństwa) i przenieść go za pomocą wanilia 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); 
    } 
Powiązane problemy