内容部分,由于具体内容也是使用模板定义出来,所以这里也是使用 [ngTemplateOutlet] 渲染出来。
<div class="tab-content">
<ng-template ngFor let-tab [ngForOf]="tabs">
<div
class="tab-pane {{tab.id === activeId ? 'active' : null}}"
*ngIf="!destroyOnHide || tab.id === activeId"
role="tabpanel"
[attr.aria-labelledby]="tab.id" id="{{tab.id}}-panel">
<ng-template [ngTemplateOutlet]="tab.contentTpl?.templateRef"></ng-template>
</div>
</ng-template>
</div>投影内容需要在 Content 类型的事件中处理。
ngAfterContentChecked() {
// auto-correct activeId that might have been set incorrectly as input
let activeTab = this._getTabById(this.activeId);
this.activeId =
activeTab ? activeTab.id : (this.tabs.length ? this.tabs.first.id : null);
}两个指令定义
指令的定义非常简单,就是获取模板的引用,以便后继使用。
可以看到属性名称为 templateRef
@Directive({selector: 'ng-template[ngbTabTitle]'})
export class NgbTabTitle {
constructor(public templateRef: TemplateRef<any>) {}
}这是
[ngbTabContent] 的定义,与上面相同,依然是定义了属性 templateRef。
@Directive({selector: 'ng-template[ngbTabContent]'})
export class NgbTabContent {
constructor(public templateRef: TemplateRef<any>) {}
}Tab 定义
元素型的指令,所以连模板都没有了。
@Directive({selector: 'ngb-tab'})内容是投影进来的。
由于在 tab 中使用了模板,并且使用指令来标识出来,它们定义在组件的模板之内,所以这里使用了 ContentChildren 来识别。
@ContentChildren(NgbTabTitle, {descendants: false}) titleTpls: QueryList<NgbTabTitle>;
@ContentChildren(NgbTabContent, {descendants: false}) contentTpls: QueryList<NgbTabContent>以后就可以使用 titleTpls 和 contentTpls 来使用模板了。
由于是内容,需要在 content 的事件中处理,实际上,在每个页签中,我们只有一个标题和一个内容的声明。
ngAfterContentChecked() {
// We are using @ContentChildren instead of @ContentChild as in the Angular version being used
// only @ContentChildren allows us to specify the {descendants: false} option.
// Without {descendants: false} we are hitting bugs described in:
// https://github.com/ng-bootstrap/ng-bootstrap/issues/2240










