基于Material的自定义Tab组元素:如何访问Slot中的Template?
Nice approach using templates for dynamic tabs—let's get this working for you! Since you're using Angular (with Material's mat-tab-group), accessing projected templates requires using Angular's content querying APIs instead of trying to read slot content directly. Here's how to do it properly:
1. Use Angular's <ng-template> (Idiomatic Approach)
First, switch from native <template> to Angular's <ng-template>—this plays nicer with Angular's template system. We'll also add a custom directive to each template to track its label and content:
<my-tabgroup> <ng-template appTab label="Tab 1"> <p>Tab 1 content</p> </ng-template> <ng-template appTab label="Tab 2"> <p>Tab 2 content</p> </ng-template> <ng-template appTab label="Tab 3"> <p>Tab 3 content</p> </ng-template> </my-tabgroup>
2. Create a Tab Directive
This small directive will attach to each <ng-template> to capture the tab label and expose the template reference:
import { Directive, Input, TemplateRef } from '@angular/core'; @Directive({ selector: '[appTab]' }) export class TabDirective { @Input() label!: string; constructor(public templateRef: TemplateRef<any>) { } }
3. Query Projected Templates in Your my-tabgroup Component
Use @ContentChildren to fetch all instances of the TabDirective in your component's projected content. Then render each tab using ngTemplateOutlet to inject the template content:
import { Component, ContentChildren, QueryList, AfterContentInit } from '@angular/core'; import { TabDirective } from './tab.directive'; @Component({ selector: 'my-tabgroup', template: ` <mat-tab-group> <mat-tab *ngFor="let tab of tabs" [label]="tab.label"> <ng-container [ngTemplateOutlet]="tab.templateRef"></ng-container> </mat-tab> </mat-tab-group> ` }) export class MyTabgroupComponent implements AfterContentInit { @ContentChildren(TabDirective) tabs!: QueryList<TabDirective>; ngAfterContentInit() { // This lifecycle hook ensures projected content is loaded console.log('Loaded tabs:', this.tabs); } }
4. If You Need to Stick with Native <template>
If you must use the native <template> element, you can query them as DOM elements and convert them to Angular TemplateRefs:
Update your markup to add a template reference to each native template:
<my-tabgroup> <template #tabTemplate label="Tab 1"> <p>Tab 1 content</p> </template> <template #tabTemplate label="Tab 2"> <p>Tab 2 content</p> </template> </my-tabgroup>
Then adjust your component code to handle native templates:
import { Component, ContentChildren, ElementRef, QueryList, AfterContentInit, ViewContainerRef, TemplateRef } from '@angular/core'; @Component({ selector: 'my-tabgroup', template: ` <mat-tab-group> <mat-tab *ngFor="let tab of tabGroups" [label]="tab.label"> <ng-container [ngTemplateOutlet]="tab.template"></ng-container> </mat-tab> </mat-tab-group> ` }) export class MyTabgroupComponent implements AfterContentInit { @ContentChildren('tabTemplate') nativeTemplates!: QueryList<ElementRef<HTMLTemplateElement>>; tabGroups: { label: string; template: TemplateRef<any> }[] = []; constructor(private viewContainerRef: ViewContainerRef) { } ngAfterContentInit() { this.nativeTemplates.forEach(templateEl => { const label = templateEl.nativeElement.getAttribute('label'); // Convert native template to Angular-compatible TemplateRef const templateRef = this.viewContainerRef.createEmbeddedView(templateEl.nativeElement).templateRef; this.tabGroups.push({ label, template: templateRef }); }); } }
Key Notes
@ContentChildrenis Angular's way of accessing content projected into your component—this replaces native slot access for Angular-specific use cases.ngTemplateOutletis how you render Angular templates dynamically, which is perfect for injecting your tab content into eachmat-tab.
内容的提问来源于stack exchange,提问作者Andrew

