基于 Angular Material 的 Data Grid 设计实现

image

自 Extensions 组件库发布以来,Data Grid 成为了使用及咨询最多的组件。最开始 Data Grid 的设计非常简陋,经过一番重构,组件质量有了质的提升。
Extensions 组件库: https://github.com/ng-matero/extensions
Data Grid 示例: https://ng-matero.github.io/extensions/data-grid

距离 Extensions Data Grid 重构已经过去了两个多月,因工作忙碌而迟迟没有介绍 Extensions Data Grid 的细节。这几天又重构了一下官网示例,目前的 API 文档放在了 gitbook 上,暂时还没有和官网整合,国内访问会比较慢。本文会介绍 Data Grid 的使用方法及比较好的一些功能实现。说点题外话,开发一款插件最大的难度不在于功能的实现,而在于如何去设计插件

什么是 Data Grid?

Data Grid 本质上就是通过 数据+列定义+配置项 来渲染表格的插件。这比写一堆 DOM 结构要简洁很多,可以说是 CRUD 业务中的大杀器之一。目前市面上功能最全的 Data Grid 是 ag-grid,很多组件库也有自己的 Data Grid 实现,比如 Ignite UIKendo UI。但是市面上这些优秀的插件基本都要收费,另外就是遇到变态需求时,第三方插件的功能定制会遇到很多问题,这也是我自研 Data Grid 的初衷。

Angular Material 对于 table 的封装已经足够灵活,但是模板的定义依然很繁琐,也缺少很多刚需功能。Extensions Data Grid 几乎整合了 Angular Material 表格的所有功能,同时又增加了很多实用功能。

Extensions Data Grid 简介

Extensions Data Grid 的功能实现参考了 ag-grid 以及其它插件,重构时对变量及参数命名进行了很细致的考究。目前 Extensions Data Grid 已经实现的功能如下:

  • Paging(分页,包括前端分页和后端分页)
  • Sorting(排序,目前只支持单值排序)
  • Sticky columns(列的固定)
  • Column hiding(列的显示隐藏)
  • Column moving(列的移动排序)
  • Checkbox selection(数据选择)
  • Row selection(行选取,可多选)
  • Cell selection(单元格选取,暂时只支持单选)
  • Expandable row(可展开的表格行)
  • Data Formatting(数据格式化)
  • Customized cell(自定义单元格)
  • Template(各种模板)

因文章篇幅有限,本文主要介绍一些重点功能,其它功能可以参考官网示例。

基本用法

image

官网示例:Basic

定义组件参数

<mtx-grid [data]="list" 
          [columns]="columns">
</mtx-grid>

定义数据及列

export class AppComponent {
  columns: MtxGridColumn[] = [
    { header: 'Name', field: 'name' },
    { header: 'Weight', field: 'weight' },
    { header: 'Gender', field: 'gender' },
    { header: 'Mobile', field: 'mobile' },
    { header: 'City', field: 'city' },
  ];

  list = EXAMPLE_DATA;
}

补充介绍一下,市面上 Data Grid 定义列的方式主要有两种:

1、JS 定义,比如 ag-grid

var gridOptions = {
    // define 3 columns
    columnDefs: [
        { headerName: 'Athlete', field: 'athlete' },
        { headerName: 'Sport', field: 'sport' },
        { headerName: 'Age', field: 'age' }
    ],

    // other grid options here...
}

2、模板定义,比如 Ignite UI

<igx-grid igxPreventDocumentScroll #grid1 [data]="data | async" [height]="'500px'" width="100%" [autoGenerate]='false' [allowFiltering]="true">
    <igx-column [field]="'Category'" [width]="'120px'"></igx-column>
    <igx-column [field]="'Type'" [width]="'150px'" [filterable]='false'></igx-column>
    <igx-column [field]="'Open Price'" [width]="'120px'" dataType="number" [formatter]="formatCurrency">
    </igx-column>
    <igx-column [field]="'Price'" [width]="'120px'" dataType="number" [formatter]="formatCurrency"></igx-column>
</igx-grid>

权衡各种利弊,Extensions Data Grid 选择了第一种定义方法,接口定义如下:

export interface MtxGridColumn {
  field: string;
  header?: string;
  hide?: boolean;
  disabled?: boolean;
  pinned?: 'left' | 'right';
  left?: string;
  right?: string;
  width?: string;
  resizable?: boolean;
  sortable?: boolean | string;
  type?: 'tag' | 'button' | 'link' | 'image' | 'number' | 'currency' | 'percent' | 'boolean';
  tag?: MtxGridColumnTag;
  buttons?: MtxGridColumnButton[];
  formatter?: (rowData: any, colDef?: any) => void;
  cellTemplate?: TemplateRef<any> | null;
  showExpand?: boolean;
  description?: string;
  i18n?: string;
  summary?: ((colData: any, colDef?: any) => void) | string;
}

模板

image

模板是 angular 组件极其灵活的一个功能。大部分优秀的第三方组件都具有自定义模板的能力,而在 Data Grid 中,模板更是一个不可或缺的功能。Extensions Data Grid 的模板功能已经比较完善,单元格模板除了基本的方法外,还增加了更为简单易用的方法。

普通方法

<mtx-grid [data]="list"
          [columns]="columns">
</mtx-grid>

<ng-template #statusTpl let-row let-index="index" let-col="colDef">
  <mat-slide-toggle [checked]="row.status">Slide me!</mat-slide-toggle>
</ng-template>
export class AppComponent implements OnInit {
  @ViewChild('statusTpl', { static: true }) statusTpl: TemplateRef<any>;

  columns: MtxGridColumn[] = [];

  list = EXAMPLE_DATA;

  ngOnInit() {
    this.columns = [
      { header: 'Name', field: 'name' },
      { header: 'Weight', field: 'weight' },
      { header: 'Gender', field: 'gender' },
      { header: 'Mobile', field: 'mobile' },
      { header: 'City', field: 'city' },
      { header: 'Status', field: 'status', cellTemplate: this.statusTpl },
    ];
  }
}

官网示例:Custom cell template

引用模板实例是一种很常见的思路,但是弊端就是必须将列定义写在 ngOnInit 中,而且要先引用所用的自定义模板实例。这种写法很不灵活。

升级方案

<mtx-grid [data]="list"
          [columns]="columns"
          [cellTemplate]="{ city: cityTpl }">
</mtx-grid>

<ng-template #cityTpl let-row let-index="index" let-col="colDef">
  <button mat-raised-button color="primary">{{row.city}}</button>
</ng-template>

官网示例:Custom cell template 2

这种方法直接在组件参数中定义了模板实例 [cellTemplate]="{ city: cityTpl }",其中 city 是列定义中的 field,除此之外不需要再写其它任何代码,非常简单!

除了单元格模板之外,还有 headerTemplate、summaryTemplate、toolbarTemplate 等,可以满足大部分的个性化需求,详情见官网示例。

选取

image

官网示例:Row selectable

表格的行选取是一个很常见的需求,用途广泛。默认开启单元格选取,可以设置 [cellSelectable]="false" 以关闭单元格选取。

通过 [rowSelectable]="true" 可以开启行选取。

<mtx-grid [data]="list"
          [columns]="columns"
          [rowSelectable]="rowSelectable"
          (rowSelectionChange)="log($event)"
          (cellSelectionChange)="log($event)">
</mtx-grid>

通过 [multiSelectable]="true" 可以设置多选行。这里有一个细节,按住 ctrl 并单击才可以多选,或者直接点击 checkbox 也可以。如果需要隐藏 checkbox,只需要设置 [hideRowSelectionCheckbox]="true"

如果初始化表格时希望默认选中某些行,则只需要定义 [rowSelected]=[...]

不可选取

image

设置不可选取行的方式有两种,一种是设置 checkbox 为 disabled,另一种是隐藏 checkbox。配置非常简单,只需要通过 rowSelectionFormatter 过滤数据即可。

<mtx-grid [data]="list"
          [columns]="columns"
          [rowSelectable]="true"
          [rowSelectionFormatter]="rowSelectionFormatter">
</mtx-grid>
export class AppComponent {
  columns: MtxGridColumn[] = [
    { header: 'Name', field: 'name' },
    { header: 'Weight', field: 'weight' },
    { header: 'Gender', field: 'gender' },
    { header: 'Mobile', field: 'mobile' },
    { header: 'City', field: 'city' },
  ];

  list = EXAMPLE_DATA;

  rowSelectionFormatter: MtxGridRowSelectionFormatter = {
    disabled: (data) => data.name === 'Boron',
    hideCheckbox: (data) => data.name === 'Helium',
  };
}

行展开

image

官网示例:Expandable row

行展开的实现借助了 Angular Material 表格的 multiTemplateDataRows 参数,实现细节很多。Data Grid 的代码如下:

设置 expandableexpansionTemplate

<mtx-grid [data]="list"
          [columns]="columns"
          [expandable]="true"
          [expansionTemplate]="expansionTpl">
</mtx-grid>

<ng-template #expansionTpl let-row>
  {{row.name}}
</ng-template>

在列定义中设置 showExpand, 确定在哪个列显示展开符号。

export class AppComponent {
  columns: MtxGridColumn[] = [
    { header: 'Name', field: 'name', showExpand: true },
    { header: 'Weight', field: 'weight' },
    { header: 'Gender', field: 'gender' },
    { header: 'Mobile', field: 'mobile' },
    { header: 'City', field: 'city' },
  ];

  list = EXAMPLE_DATA;
}

列操作

image

官网示例:Column hiding & moving

列的显示隐藏以及排序是非常常见的需求,这类需求曾被产品经理折磨了无数次。目前的列操作 UI 只有菜单方式,之后还会添加侧边栏的 UI,暂时不支持列的横向拖拽。

列的操作完全可以移到组件之外,通过设置 columns 实现,并不一定非要用 Data Grid 集成好的功能。

总结

因篇幅有限,很多 Extensions Data Grid 的功能没有详细介绍。从我遇到的需求来看,目前的 Data Grid 已经可以覆盖九成的需求了,还有很多高级功能正在开发当中,欢迎大家提出建设性意见。如果大家在使用组件的过程中遇到问题,可以在 GitHub 中提交 issues 或者进讨论群提问。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270