The Long Way To Angular(下文以 TLWTA代指)系列停更很久,现在考虑继续。
在我个人的工作中,还是非常愿意使用 Angular 去开发 Web 应用的,只不过主观因素和客观因素之下,实际 Angular 在我的工作中很难起到主导地位。原因我也在之前的文章中提过了,这样一个复杂而笨重的框架不管是对于我的工作还是我的同事来说,都很难成为一个可靠的选择方案。
比如我个人而言,如果是短平快的应用,我会选择 React 去开发;如果是各种管理平台,我会选择 Vue 去开发;而各种插件式的开发需求,我更愿意选择 lit-element 等 Web Components 框架去开发。以至于到现在,我还没有找到一个打定主意把 Angular 作为唯一选择的场景。
这种情况下,TLWTA 就以我平时开发过程中的各种经历和问题来展开会更好,比如 Tabs 组件。
Tabs 组件设计
Tabs(标签页)组件也算是常用网页组件之一了,它可以很方便的展示同一类内容的不同区块。 一般来说,Tabs 组件包含两个部分 Tab 标签和 Tab 内容区。以 Material Design 中的 Tabs 组件为例,大概就是下图这个样子: Tab 标签和内容是一一对应的,那么在组件设计的时候,就需要解决它们的对应关系,那么最基本的使用方式就是:
<!-- app.component.html -->
<tabs>
<tab title="">
<p>...</p>
</tab>
<tab title="">
<p>...</p>
</tab>
</tabs>
这样在使用组件的地方,就明确了 Tab 的结构,那么这个 Tabs 的逻辑就是:
- 获取所有
<tab>
的title组成标签元素; - 获取所有
<tab>
的内容作为标签内容,展示当前激活的那一个; Angular 已经提供了很多方法来获取组件内部的其他组件,比如 slot 或者<ng-content select="">
,但是这两种方法的问题在于不方便处理多个重复元素,而 Tabs 组件的场景,重复的元素是要分开处理的。 因此我们选择另一种方式
// tabs.component.ts
class TabsComponent {
@ContentChildren(TabComponent) tabs: QueryList<TabComponent>;
}
@ContentChildren
就是做组件内部元素查询的,它需要的最基本参数就是 selector(选择器),所以直接用组件的类作为参数也是可以的,QueryList 则是它查询结果的类型。
ngAfterContentInit
的回调中,tabs
属性就已经可用了。
// tabs.component.ts
selectTab(tab) {
this.tabs.toArray().forEach((t) => t.active = false);
tab.active = true;
}
ngAfterContentInit() {
const activeTabs = this.tabs.filter((tab) => tab.active);
if (activeTabs.length === 0) {
this.selectTab(this.tabs.first);
}
}
通过上面的逻辑,我们就已经实现了选择某个tab的逻辑,并且默认展示了第一个tab的内容。注意 @ContentChildren
查询的结果不是直接的数组,记得使用 .toArray()
方法得到对应数组。
然后我们编写对应的 tab 标签的模版:
<!-- tabs.component.html -->
<div class="tabs">
<div
class="tab"
*ngFor="let tab of tabs"
(click)="selectTab(tab)"
[class.active]="tab.active"
>
<span class="tab-text">{{tab.title}}</span>
</div>
</div>
<ng-content></ng-content>
这里 <ng-content>
就是所有的 <tab>
,所以我们只需要在 tab 组件中,根据它的 active 状态展示他就是可以了
<!-- tab.component.html -->
<div [hidden]="!active" class="pane">
<ng-content></ng-content>
</div>
在这篇文章中,我们学到了 <ng-content>
, @ContentChildren
等特性的使用。
demo: