2017-06-29 8 views
1

Ich habe versucht, eine Tab-Ansicht zu erstellen, und die Gedankenprojektion könnte ein guter Ansatz sein. Ich habe es von dieser article gelernt. Ich dachte, dass ich es dynamisch machen könnte, indem ich nur ein bestimmtes Array von Komponenten eingab und sie als die Seite für die ausgewählte Registerkarte angezeigt würden.Angular 2 - ContentChidren der übergeordneten Komponente sind nicht definiert, wenn die untergeordnete Komponente dynamisch erstellt wird.

Hier ist mein Versuch, dies zu tun:

@Component({ 
    selector: 'my-app', 
    template:` 
    <h1>Experiments</h1> 
    <button class="add-button" (click)="add()">Add</button>` 
}) 
export class App { 

    components:Array<any> = [PrepSetupTab,FinalizeTab] 

    constructor(private cdr: ChangeDetectorRef, 
       private compFR: ComponentFactoryResolver, 
       private viewContainer: ViewContainerRef){} 

    add():void{ 
     var transTabRefs: Array<any>= [] 

     this.components.forEach(tabComponent=>{ 

      let compFactory = this.compFR.resolveComponentFactory(tabComponent); 
      let compRef = this.viewContainer.createComponent(compFactory); 
      let tabFactory = this.compFR.resolveComponentFactory(Tab); 

      let transcludedTabRef = this.viewContainer.createComponent(tabFactory,this.viewContainer.length - 1, undefined, [[compRef.location.nativeElement]]); 
      transTabRefs.push(transcludedTabRef.location.nativeElement); 
     }) 

     let tabsFactory = this.compFR.resolveComponentFactory(Tabs); // notice this is the tabs not tab 
     this.viewContainer.createComponent(tabsFactory,0,undefined,[transTabRefs]); 

    } 


} 

Randnotiz, add() als ein Klick-Handler-Taste ist nicht beabsichtigt, Funktionalität andere diese Fehler zu vermeiden: "EXCEPTION: Expression has changed after it was checked". Ich bekomme diesen Fehler, wenn ich ihn in irgendeinen Lebenszyklus-Haken stecke. Obwohl das eine Herausforderung ist, mit der ich mich später befassen muss.

Also im Grunde ich die einzelnen Komponenten im Array erstellen, und dann stecke ich sie als ng-Inhalt für jede erstellte Registerkarte Komponente. Dann nimm jede Tab-Komponente und stecke sie in ng-content für Tabs Component.

Das Problem ist Tabs Component findet nicht die contentChildren der dynamisch erstellten Tab Kinder. Hier ist der Code für die Tabs-Komponente, in der die untergeordneten Inhalte nicht definiert sind.

@Component({ 
    selector: 'tabs', 
    template:` 
    <ul class="nav nav-tabs"> 
     <li *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active"> 
     <a>{{tab.title}}</a> 
     </li> 
    </ul> 
    <ng-content></ng-content> 
    ` 
}) 
export class Tabs implements AfterContentInit { 

    @ContentChildren(Tab) tabs: QueryList<Tab>; 

    // contentChildren are set 
    ngAfterContentInit() { 
    // get all active tabs 
    let activeTabs = this.tabs.filter((tab)=>tab.active); 

    // if there is no active tab set, activate the first 
    if(activeTabs.length === 0) { 
     this.selectTab(this.tabs.first); 
    } 
    } 

    selectTab(tab: Tab){ 
    // deactivate all tabs 
    this.tabs.toArray().forEach(tab => tab.active = false); 

    // activate the tab the user has clicked on. 
    tab.active = true; 
    } 

} 

Es scheint ziemlich klar zu mir zu sein, dass die Tab-Komponente dann zu einem anderen Zeitpunkt erstellt, wenn ich sich als Inhalt Kinder aus der Tabs Komponente zugreifen muß. Ich habe auch versucht, die ChangeDetectionRef.changeDetect(), aber das hilft nicht.

Vielleicht macht dies alles durch Content-Projektion ist nicht der einfachste Weg oder am besten, so dass ich für Vorschläge offen bin. Hier ist die plunk, danke!

+0

'@ ContentChildren' nicht mit dynamischer Erstellung Komponente arbeiten. Das ist per Entwurf – yurzui

+0

http://plnkr.co/edit/t1GnzDDR2s5g8PSDojXv?p=preview – yurzui

+0

Wow, InitContent Ich habe das vorher nicht gesehen! Und wenn Sie daran denken, werden Sie vielleicht etwas von diesem Code erkennen, da ich denke, dass ich es von einem früheren Plunk bekommen habe :). –

Antwort

0

@ContentChildren funktioniert nicht für dynamisch erstellte Komponenten.

Warum?

Das ist, weil Kandidaten für die Projektion zur Kompilierzeit bekannt sein müssen.

Dies ist die Funktion, die zur Berechnung verwendet wird ContentChildren

function calcQueryValues(view, startIndex, endIndex, queryDef, values) { 
    for (var /** @type {?} */ i = startIndex; i <= endIndex; i++) { 
     var /** @type {?} */ nodeDef = view.def.nodes[i]; 
     var /** @type {?} */ valueType = nodeDef.matchedQueries[queryDef.id]; 
     if (valueType != null) { 
      values.push(getQueryValue(view, nodeDef, valueType)); 
     } 

Es nutzt Knoten, die in viewDefinition für die Komponente deklariert sind. Wenn Sie tabs Komponente über createComponent erstellt und projektierbare Knoten manuell übergeben haben Sie nicht viewDefinition Factory für Tabs-Komponente geändert und daher eckig wird nicht über dynamische Knoten bewusst sein.

Eine mögliche Lösung könnte manuell Ihre Tabs

Initialisieren Sie können beobachten, in diesem Plunkr

0

Werfen Sie einen Blick auf das Beispiel, das ich hier gebe dynamically add elements to DOM, vielleicht finden Sie dort etwas hilfreiches für Ihr Problem. An diesem Punkt in Ihrem PLNKR ist das Tabs-Array leer.

Verwandte Themen