2017-10-10 3 views
1

Ich versuche, eine wiederverwendbare Registerkarte Komponente zu erstellen und bin verwirrt darüber, wie über mehrere ContentChildren (korrigiert) innerhalb der Komponente, um sie in HTML zu wickeln.Angular 2 - Wrap Komponente Kinder

Ich habe eine Komponente aus meiner Sicht

bekam
<demo-tabs> 
    <a routerLink="/some/link">Tab 1</a> 
    <a routerLink="/some/other/link">Tab 2</a> 
    <a routerLink="/some/third/link">Tab 3</a> 
</demo-tabs> 

, die ich wie so machen möchten:

<ul> 
    <li><a routerLink="/some/link">Tab 1</a></li> 
    <li><a routerLink="/some/other/link">Tab 2</a></li> 
    <li><a routerLink="/some/third/link">Tab 3</a></li> 
<ul> 

Es scheint nicht, wie ich Inhalt in ng-content einbetten können die ist was ich zuerst ausprobiert habe, und die folgende Vorlage explodiert mit ExpressionChangedAfterItHasBeenCheckedError

@Component({ 
    selector: 'demo-tabs', 
    template: `<ul class="tabs"> 
    <li *ngFor="let a of links"> 
     {{a}} 
    </li> 
    </ul>`}) 
export class TabsComponent implements OnInit { 
    @ContentChildren('a') links: TemplateRef<any>; // corrected. I originally had this as @ViewChildren 

    constructor() { } 

    ngOnInit() { 
    } 

} 

Antwort

2

Zuerst sind diese Links nicht @ViewChildren() für die Tabs-Komponente - sie sind @ContentChildren(), weil @ViewChildren() in der Vorlage der Komponente deklariert werden muss, während @ContentChildren() von einer externen Deklaration kommt - nur wie du es getan hast.

Um Inhalte auf diese Weise trennen zu können, müssen Sie alle Elemente mit einer sehr einfachen benutzerdefinierten Strukturanweisung (wie folgt) "markieren", damit Sie sie als Liste separater Elemente in Ihrer TabsComponent erhalten.

link.directive.ts

import {Directive, TemplateRef} from '@angular/core'; 

@Directive({ 
    selector: '[appLink]' 
}) 
export class AppLinkDirective { 
    constructor(public template: TemplateRef<any>) { } 
} 

Dies ist eine strukturelle Richtlinie, die HTML-Vorlage als DI Injektion Token empfangen kann. Die Vorlage dieses Elements muss tatsächlich in die Vorlage von TabsComponent gerendert werden.

Dann lassen Sie uns unsere Einzelteile markieren:

app.component.html

<app-demo-tabs> 
    <a *appLink routerLink="/some/link">Tab 1</a> 
    <a *appLink routerLink="/some/other/link">Tab 2</a> 
    <a *appLink routerLink="/some/third/link">Tab 3</a> 
</app-demo-tabs> 

Und schließlich, so dass sie in der Vorlage Komponente machen:

tabs.component.ts

import {Component, ContentChildren, OnInit, QueryList} from '@angular/core'; 
import {AppLinkDirective} from './link.directive'; 

@Component({ 
    selector: 'app-demo-tabs', 
    template: ` 
     <ul class="tabs"> 
      <li *ngFor="let link of links"> 
       <ng-template [ngTemplateOutlet]="link?.template"></ng-template> 
      </li> 
     </ul>` 
}) 
export class TabsComponent implements OnInit { 

    @ContentChildren(AppLinkDirective) 
    links: QueryList<AppLinkDirective>; 

    constructor() { 
    } 

    ngOnInit() { 
    } 

} 

Und natürlich müssen Sie diese Anweisung in ein Modul importieren, damit es in der Vorlage verwendet werden kann.

+0

Einige alternative Lösung https://stackblitz.com/edit/angular-wqjlbw?file=app%2Fapp.module.ts aber ich mag Ihre Antwort – yurzui

+0

@ yurzui, ja, das ist natürlich möglich, und keine schlechte Wahl überhaupt, aber ich mag eigentlich so wenig wie möglich in den html - so liest es sich klarer und wichtige Dinge sind weniger unter Haufen von sekundärem HTML-Zeug versteckt. –

+0

Das hat perfekt funktioniert. Ich habe auch die Lösung von yurzui angeschaut und ich sehe, wie das auch funktioniert, aber ich stimme zu, dass es klarer ist (für einen Anfänger wie mich). Vielen Dank! – BLSully