背景及吐槽:
今年有机会再次接触 angualr 这个框架,想起第一次接触 ng 还是 16 年读书的时候,当时还是 ng1,然后学起来特别辛苦,学习曲线特别陡峭;而今年有一个项目重构直接采用了 angular6,而后面该项目后面由我负责开发和维护,然后我又重新再学习了 ng6,本以为有 ng1 的基础,学起来会好一些,然并卵,学习的曲线特别陡峭,但还是最后将 ng6 啃下来(很不情愿去学的,但没办法)。回归到项目,该项目没有引入其他组件库,所以很多基础组件都是自己开发(用 ng 开发那种酸爽很带劲),其中 table 组件让我思考了差不多两个星期,最后才开发出来,吐槽完毕,接下来就介绍一下我的做法,我的做法不一定最正确。
形式:主要参考 element 里面的 table 组的格式:
vue:
<el-table :data=”tableData”>
<el-table-column prop=”date” label=” 日期 ”></el-table-column>
<el-table-column label=” 操作 ”>
<template slot-scope=”scope”>
<el-button @click=”handleClick(scope.row)” type=”text” size=”small”> 查看 </el-button>
</template>
</el-table-column>
</el-table>
所以得到了 angualr 的 table 组件的格式:
<app-widget-table [data]=”tableData”>
<app-widget-table-column prop=”date” label=” 日期 ”></app-widget-table-column>
<app-widget-table-column label=” 操作 ”>
<ng-template #scope let-row=”scope”>
<ng-widget-button (click)=”handleClick(row)” type=”text” size=”small”> 查看 </el-button>
</ng-template>
</app-widget-table-column>
</app-widget-table>
在 angular 的 table 组件中,最为困难就是 ng-template 如何将作用域绑定到 ng-widget-button 组件中;
关键点知识讲解:
ng-content:可以将父组件中所包含的所有子组件,都插入 table 组件中 ng-container 所在的位置,跟 vue 中的 slot 很像;
ng-container: 可以作为一个组件的模板,跟 vue 里面的 template 组件很像;
ng-template:该东西,是整个组件中最为麻烦的一个东西,直接使用它,会没有任何效果,必须要和 TemplateRef 和 ngTemplateOutlet 一起使用,才有有效果,主要是作为模板并引入作用域,具体原理可以看一下官方文档(https://www.angular.cn/api)
TemplateRef:主要是用来获取 ng-template 组件的引用;
ngTemplateOutlet:将 ng-template 的内容在 html 页面展示出来,并绑定变量,就像 vue 中的 router-view;
QueryList:获取 table 组件中所有的内容指引;
ContentChildren:内容映射的接口,针对多个子元素采用
ContentChild:内容映射的接口,针对单个子元素采用
先对 app-widget-table-column 组件进行分析:该组件的作用就是为了运输数据,并且引入内容,该组件本身是不会有任何操作和逻辑,就是一个运输工;
table-column.component.html:
<ng-container></ng-container>
table-column.component.ts:
import {Component, Input, Output, TemplateRef, ContentChild, AfterContentInit} from ‘@angular/core’;
@Component({
selector: ‘app-widget-table-column’,
templateUrl: ‘./table-column.component.html’,
styleUrls: [‘./table-column.component.less’],
preserveWhitespaces: false
})
export class TableColumnComponent implements AfterContentInit {
constructor() {
}
@Input()
label: string;
@Input()
prop: string;
@Input()
class: string;
@Input()
style: object;
@ContentChild(‘scope’) // 获取 ng-template 组件的一个本地变量,并修饰 scope 对象
scope: TemplateRef<any>; // 获取 ng-template 的指引,主要是其内容,any 表示该指可以是任何内容
ngAfterContentInit(): void {}
}
table.component.html
<div>
<div>
<ng-content></ng-content> // 主要是用来引入整个 table 组件的内容,但不会在页面显示任何内容
</div>
<table class=”table”>
<thead>
<tr>
<th *ngFor=”let label of labelList”>{{label}}</th> // 类似于 v -for,主要讲 table-cloumn 的所有 label 搜集,并展示
</tr>
</thead>
<tbody *ngIf=”data.length > 0″>
<ng-container *ngFor=”let item of data; let i = index”>
<tr>
<ng-container *ngFor=”let row of tableColumn[‘_results’]”>
<td *ngIf=”row.prop” [ngStyle]=”row.style” [ngClass]=”row.class”>{{item[row.prop]}}</td> // 直接展示
<td *ngIf=”row.scope” [ngStyle]=”row.style” [ngClass]=”row.class”>
<ng-container *ngTemplateOutlet=”row.scope; context: {$implicit: {}, scope: data[i]}”>
</ng-container> // 展示 ng-template 的内容
</td>
</ng-container>
</tr>
</ng-container>
</tbody>
</table>
<div *ngIf=”data.length === 0″ class=”none-data”> 暂无数据!</div>
</div>
table.component.ts:
import {Component, OnInit, Input, Output, ContentChildren, AfterContentInit, ViewChild, AfterViewInit, QueryList} from ‘@angular/core’;
import {TableColumnComponent} from ‘../table-column/table-column.component’;
@Component({
selector: ‘app-widget-table’,
templateUrl: ‘./table.component.html’,
styleUrls: [‘./table.component.less’],
preserveWhitespaces: false
})
export class TableComponent implements OnInit, AfterContentInit {
constructor() {
}
@ContentChildren(TableColumnComponent)
tableColumn: QueryList<TableColumnComponent>; // 获取 table-cloumn 组件的所有实例
@Input()
data: object[];
labelList: string[] = [];
ngOnInit(): void {
if (!(this.data instanceof Array)) {
throw new Error(‘the data into TableComonent must be Array!’);
}
}
ngAfterContentInit(): void {
this.labelList = this.tableColumn[‘_results’].map(item => item.label);
}
}
虽然看起来这两个组件的代码不多,但里面的逻辑却比较绕,这也证明了 ng 用起来十分难上手,不过真的称赞的是,ng 采用 ts 和 rx,用上手确实是比较爽。
这两个组件目前还是比较粗糙,功能和特性也不是特别多,只能满足一般表格的需求,后续会继续完善该组件以及其他项目中用 ng 来开发的基础组件,希望能沉淀出一套 ng 的组件库。以上若有不正确的地方,欢迎指出。