You don't see anything because the mat-tab
components need to be direct children of the mat-tab-group
component else it is not aware that any tabs exist. For this reason you need to approach things differently. You need to get the content from your custom components and then render everything inside the wrapper component. I think the simplest way is to get the content as templates and it is also very close to what you created. I will start with the lowest components and move up.
The app-item-content
component can be as you created it. The app-item-header
needs to be changed. Like for the tabs also the mat-tab-label
directive needs to be applied directly inside the mat-tab-group
. That is why I removed it from the template.
<ng-template>
{{title}}
</ng-template>
The main change is that I expose the ng-template
inside the component also as a property in the code using the ViewChild decorator. This enables the access of the template also from the outside the component.
@Component({
selector: 'app-item-header',
templateUrl: './item-header.component.html',
styleUrls: ['./item-header.component.css']
})
export class ItemHeaderComponent implements OnInit {
@ViewChild(TemplateRef) public headerTemplate: TemplateRef<any>;
@Input() title:string = ''
}
After you can move to the app-item
component. Here there is also just a small change in the template. Using the same approach as in the header component you can get the template for the item content.
<ng-template>
<ng-content select="app-item-content"t></ng-content>
<ng-template>
In code you can then again use the ViewChild decorator to get the content template as before. The header is a child component of the item to get it you need to use a different but similar ContentChild decorator to get access to the header component which provides the header template.
@Component({
selector: 'app-item',
templateUrl: './item.component.html',
styleUrls: ['./item.component.css']
})
export class ItemComponent implements OnInit {
@ViewChild(TemplateRef) public contentTemplate: TemplateRef<any>;
@ContentChild(ItemHeaderComponent) public itemHeader: ItemHeaderComponent;
}
At this point you have access to everything you need to render the contents inside the mat-tab
components. In the template you need to render the mat-tab-group
and a mat-tab
corresponding to each app-item
component and the contents from the templates that were exposed before.
<mat-tab-group>
<mat-tab *ngFor="let item of appItems">
<ng-template mat-tab-label>
<ng-container *ngTemplateOutlet="item.itemHeader.headerTemplate"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="item.contentTemplate"></ng-container>
</mat-tab>
</mat-tab-group>
As you can see it is quite straight forward you create a tab for each item and render inside the contents from the item. A bit of a tricky thing is the header you need to render the template inside another ng-template
to be able to add the mat-tab-label
directive that it is recognized as header content.
To get all the app-item
components inside the wrapper component you use the ContentChildren decorator which gives a list of components inside the content. Like in previous examples we just add a property in code the rest is handled by Angular.
@Component({
selector: 'app-wrapper',
templateUrl: './wrapper.component.html',
styleUrls: ['./wrapper.component.css']
})
export class WrapperComponent implements OnInit {
@ContentChildren(ItemComponent) public appItems: QueryList<ItemComponent>;
}
I have created also a fork of you StackBlitz where you can see it working.