深入浅析ng-bootstrap 组件集中 tabset 组件的实现分析

2020-05-16 18:42:48易采站长站整理

内容部分,由于具体内容也是使用模板定义出来,所以这里也是使用 [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