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


Posted in Javascript onJuly 19, 2019

ng-bootstrap: tabset

 本文介绍了 ng-bootstrap 项目中,tabset 的实现分析。

使用方式

<ngb-tabset> 作为容器元素,其中的每个页签以一个 <ngb-tab> 元素定义,在 <ngb-tabset> 中包含若干个 <ngb-tab> 子元素。

<ngb-tab> 元素中,使用 <ng-template> 模板来定义内容,内容分为两种:标题和内容。

标题使用 [ngbTabTitle] 指令来声明,或者在 <ngb-tab> 元素上使用 title 属性声明。

内容使用 [ngbTabContent] 指令声明。

<ngb-tabset>
 <ngb-tab title="Simple">
 <ng-template ngbTabContent>
  <p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth
  master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh
  dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum
  iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
 </ng-template>
 </ngb-tab>
 <ngb-tab>
 <ng-template ngbTabTitle><b>Fancy</b> title</ng-template>
 <ng-template ngbTabContent>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid.
  <p>Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table
  craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl
  cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia
  yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean
  shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero
  sint qui sapiente accusamus tattooed echo park.</p>
 </ng-template>
 </ngb-tab>
 <ngb-tab title="Disabled" [disabled]="true">
 <ng-template ngbTabContent>
  <p>Sed commodo, leo at suscipit dictum, quam est porttitor sapien, eget sodales nibh elit id diam. Nulla facilisi. Donec egestas ligula vitae odio interdum aliquet. Duis lectus turpis, luctus eget tincidunt eu, congue et odio. Duis pharetra et nisl at faucibus. Quisque luctus pulvinar arcu, et molestie lectus ultrices et. Sed diam urna, egestas ut ipsum vel, volutpat volutpat neque. Praesent fringilla tortor arcu. Vivamus faucibus nisl enim, nec tristique ipsum euismod facilisis. Morbi ut bibendum est, eu tincidunt odio. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris aliquet odio ac lorem aliquet ultricies in eget neque. Phasellus nec tortor vel tellus pulvinar feugiat.</p>
 </ng-template>
 </ngb-tab>
</ngb-tabset>

可以看到,外层元素是 <ngb-tabset>

每个 tab 使用元素 <ngb-tab> 定义,tab 的内容使用 <ng-template> 模板定义, tab 中的内容分为两个部分:标题和内容。

下面是使用模板的标题

<ng-template ngbTabTitle><b>Fancy</b> title</ng-template>

标题也可以在 ngb-tab 上使用 [title] 属性定义。例如:

<ngb-tab title="Disabled" [disabled]="true">

内容部分定义,这里使用了指令 [ngbTabContent] 便于识别。

<ng-template ngbTabContent>
 <p>Sed commodo, leo at suscipit dictum, quam est porttitor sapien, eget sodales nibh elit id diam. 
 </p>
</ng-template>

TabSet 组件定义

从前面的使用可以看出,所有 tab 的定义都是 ngb-tabset 元素的内容,它们在使用时定义,而不是在 ngb-tabse 自己的模板中定义。

所以找到它们需要使用 ContentChildren 来找到。

@ContentChildren(NgbTab) tabs: QueryList<NgbTab>;

不使用 ContentChild 的原因是它没有提供 descendants 的支持。

在 bootstrap 中,每个页签 实际上渲染成两个部分,一个标题的列表,和当前显示的内容。

标题列表使用一个 ul 来处理。其中使用循环来将所有的标题显示出来。

而 titleTpl 是由模板定义的,所以,使用了 [ngTemplateOutlet] 来渲染出来。

<ul [class]="'nav nav-' + type + (orientation == 'horizontal'? ' ' + justifyClass : ' flex-column')" role="tablist">
 <li class="nav-item" *ngFor="let tab of tabs">
  <a [id]="tab.id" class="nav-link" 
   [class.active]="tab.id === activeId" 
   [class.disabled]="tab.disabled"
   href (click)="select(tab.id); $event.preventDefault()" 
   role="tab" 
   [attr.tabindex]="(tab.disabled ? '-1': undefined)"
   [attr.aria-controls]="(!destroyOnHide || tab.id === activeId ? tab.id + '-panel' : null)"
   [attr.aria-selected]="tab.id === activeId" [attr.aria-disabled]="tab.disabled">
   {{tab.title}}<ng-template [ngTemplateOutlet]="tab.titleTpl?.templateRef"></ng-template>
  </a>
 </li>
</ul>

title 部分并列使用了两种来源

{{tab.title}}<ng-template [ngTemplateOutlet]="tab.titleTpl?.templateRef"></ng-template>

内容部分,由于具体内容也是使用模板定义出来,所以这里也是使用 [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
 this.titleTpl = this.titleTpls.first;
 this.contentTpl = this.contentTpls.first;
}

See also

•tabset

总结

以上所述是小编给大家介绍的深入浅析ng-bootstrap 组件集中 tabset 组件的实现分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
JavaScript中常见陷阱小结
Apr 27 Javascript
JavaScript实现统计文本框Textarea字数增强用户体验
Dec 21 Javascript
dwz 如何去掉ajaxloading具体代码
May 22 Javascript
轻松掌握JavaScript享元模式
Aug 27 Javascript
JS函数修改html的元素内容,及修改属性内容的方法
Oct 28 Javascript
zTree树形插件异步加载方法详解
Jun 14 Javascript
JS+CSS实现网页加载中的动画效果
Oct 27 Javascript
使用 electron 实现类似新版 QQ 的登录界面效果(阴影、背景动画、窗体3D翻转)
Oct 23 Javascript
解决vue2 在mounted函数无法获取prop中的变量问题
Nov 15 Javascript
微信小程序云开发如何使用云函数生成二维码
May 18 Javascript
vuex+axios+element-ui实现页面请求loading操作示例
Feb 02 Javascript
使用node-media-server搭建一个简易的流媒体服务器
Jan 20 Javascript
bootstrap table插件动态加载表头
Jul 19 #Javascript
Vue数据驱动表单渲染,轻松搞定form表单
Jul 19 #Javascript
VueJS 取得 URL 参数值的方法
Jul 19 #Javascript
vue自定义表单生成器form-create使用详解
Jul 19 #Javascript
bootstrap table列和表头对不齐的解决方法
Jul 19 #Javascript
JS开发 富文本编辑器TinyMCE详解
Jul 19 #Javascript
更强大的vue ssr实现预取数据的方式
Jul 19 #Javascript
You might like
PHP+Mysql树型结构(无限分类)数据库设计的2种方式实例
2014/07/15 PHP
php中session与cookie的比较
2015/01/27 PHP
简述php环境搭建与配置
2016/12/05 PHP
PHP实现类似题库抽题效果
2018/08/16 PHP
JavaScript 打地鼠游戏代码说明
2010/10/12 Javascript
js解析xml字符串和xml文档实现原理及代码(针对ie与火狐)
2013/02/02 Javascript
JavaScript在XHTML中的用法详解
2013/04/11 Javascript
javascript实现文字图片上下滚动的具体实例
2013/06/28 Javascript
js数组循环遍历数组内所有元素的方法
2014/01/18 Javascript
jQuery html()方法使用不了无法显示内容的问题
2014/08/06 Javascript
javascript动态获取登录时间和在线时长
2016/02/25 Javascript
基于jquery插件编写countdown计时器
2016/06/12 Javascript
json的结构与遍历方法实例分析
2017/04/25 Javascript
详谈Angular 2+ 的表单(一)之模板驱动型表单
2017/04/25 Javascript
ReactNative Image组件使用详解
2017/08/07 Javascript
javascript兼容性(实例讲解)
2017/08/15 Javascript
AngularJS对动态增加的DOM实现ng-keyup事件示例
2018/03/12 Javascript
对vux点击事件的优化详解
2018/08/28 Javascript
JS实现计算小于非负数n的素数的数量算法示例
2019/02/26 Javascript
浅谈Vue的响应式原理
2019/05/30 Javascript
发布订阅模式在vue中的实际运用实例详解
2019/06/09 Javascript
JavaScript实现拖拽功能
2020/02/11 Javascript
python用ConfigObj读写配置文件的实现代码
2013/03/04 Python
使用PyInstaller将Python程序文件转换为可执行程序文件
2016/07/08 Python
python之线程通过信号pyqtSignal刷新ui的方法
2019/01/11 Python
Python 获取ftp服务器文件时间的方法
2019/07/02 Python
通过python改变图片特定区域的颜色详解
2019/07/15 Python
Python3远程监控程序的实现方法
2019/07/15 Python
python selenium操作cookie的实现
2020/03/18 Python
求职简历的自我评价怎样写好
2013/10/07 职场文书
中专生求职自荐信范文
2013/12/22 职场文书
公务员培训心得体会
2013/12/28 职场文书
承诺书样本
2014/08/30 职场文书
2016年秋季运动会广播稿
2015/12/21 职场文书
辞职申请书范本
2019/05/20 职场文书
TypeScript实用技巧 Nominal Typing名义类型详解
2022/09/23 Javascript