潇潇老公 發表於 2020-6-27 22:52:00

基于 Angular Material 的 Data Grid 设计实现

<p><img src="https://img2020.cnblogs.com/blog/999445/202006/999445-20200627174611906-1272921735.jpg" alt="" loading="lazy"></p>
<blockquote>
<p>自 Extensions 组件库发布以来,Data Grid 成为了使用及咨询最多的组件。最开始 Data Grid 的设计非常简陋,经过一番重构,组件质量有了质的提升。<br>
Extensions 组件库: https://github.com/ng-matero/extensions<br>
Data Grid 示例: https://ng-matero.github.io/extensions/data-grid</p>
</blockquote>
<p>距离 Extensions Data Grid 重构已经过去了两个多月,因工作忙碌而迟迟没有介绍 Extensions Data Grid 的细节。这几天又重构了一下官网示例,目前的 API 文档放在了 gitbook 上,暂时还没有和官网整合,国内访问会比较慢。本文会介绍 Data Grid 的使用方法及比较好的一些功能实现。说点题外话,<strong>开发一款插件最大的难度不在于功能的实现,而在于如何去设计插件</strong>。</p>
<h2 id="什么是-data-grid">什么是 Data Grid?</h2>
<p>Data Grid 本质上就是通过 <code>数据</code>+<code>列定义</code>+<code>配置项</code> 来渲染表格的插件。这比写一堆 DOM 结构要简洁很多,可以说是 CRUD 业务中的大杀器之一。目前市面上功能最全的 Data Grid 是 ag-grid,很多组件库也有自己的 Data Grid 实现,比如 Ignite UI,Kendo UI。但是市面上这些优秀的插件基本都要收费,另外就是遇到变态需求时,第三方插件的功能定制会遇到很多问题,这也是我自研 Data Grid 的初衷。</p>
<p>Angular Material 对于 table 的封装已经足够灵活,但是模板的定义依然很繁琐,也缺少很多刚需功能。Extensions Data Grid 几乎整合了 Angular Material 表格的所有功能,同时又增加了很多实用功能。</p>
<h2 id="extensions-data-grid-简介">Extensions Data Grid 简介</h2>
<p>Extensions Data Grid 的功能实现参考了 ag-grid 以及其它插件,重构时对变量及参数命名进行了很细致的考究。目前 Extensions Data Grid 已经实现的功能如下:</p>
<ul>
<li>Paging(分页,包括前端分页和后端分页)</li>
<li>Sorting(排序,目前只支持单值排序)</li>
<li>Sticky columns(列的固定)</li>
<li>Column hiding(列的显示隐藏)</li>
<li>Column moving(列的移动排序)</li>
<li>Checkbox selection(数据选择)</li>
<li>Row selection(行选取,可多选)</li>
<li>Cell selection(单元格选取,暂时只支持单选)</li>
<li>Expandable row(可展开的表格行)</li>
<li>Data Formatting(数据格式化)</li>
<li>Customized cell(自定义单元格)</li>
<li>Template(各种模板)</li>
</ul>
<p>因文章篇幅有限,本文主要介绍一些重点功能,其它功能可以参考官网示例。</p>
<h3 id="基本用法">基本用法</h3>
<p><img src="https://img2020.cnblogs.com/blog/999445/202006/999445-20200626102718615-789150374.jpg" alt="" loading="lazy"></p>
<blockquote>
<p>官网示例:Basic</p>
</blockquote>
<p>定义组件参数</p>
<pre><code class="language-html">&lt;mtx-grid ="list"
          ="columns"&gt;
&lt;/mtx-grid&gt;
</code></pre>
<p>定义数据及列</p>
<pre><code class="language-ts">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;
}
</code></pre>
<p>补充介绍一下,市面上 Data Grid 定义列的方式主要有两种:</p>
<p>1、JS 定义,比如 ag-grid</p>
<pre><code class="language-js">var gridOptions = {
    // define 3 columns
    columnDefs: [
      { headerName: 'Athlete', field: 'athlete' },
      { headerName: 'Sport', field: 'sport' },
      { headerName: 'Age', field: 'age' }
    ],

    // other grid options here...
}
</code></pre>
<p>2、模板定义,比如 Ignite UI</p>
<pre><code class="language-html">&lt;igx-grid igxPreventDocumentScroll #grid1 ="data | async" ="'500px'" width="100%" ='false' ="true"&gt;
    &lt;igx-column ="'Category'" ="'120px'"&gt;&lt;/igx-column&gt;
    &lt;igx-column ="'Type'" ="'150px'" ='false'&gt;&lt;/igx-column&gt;
    &lt;igx-column ="'Open Price'" ="'120px'" dataType="number" ="formatCurrency"&gt;
    &lt;/igx-column&gt;
    &lt;igx-column ="'Price'" ="'120px'" dataType="number" ="formatCurrency"&gt;&lt;/igx-column&gt;
&lt;/igx-grid&gt;
</code></pre>
<p>权衡各种利弊,Extensions Data Grid 选择了第一种定义方法,接口定义如下:</p>
<pre><code class="language-ts">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) =&gt; void;
cellTemplate?: TemplateRef&lt;any&gt; | null;
showExpand?: boolean;
description?: string;
i18n?: string;
summary?: ((colData: any, colDef?: any) =&gt; void) | string;
}
</code></pre>
<h3 id="模板">模板</h3>
<p><img src="https://img2020.cnblogs.com/blog/999445/202006/999445-20200626111959247-1923183564.jpg" alt="" loading="lazy"></p>
<p>模板是 angular 组件极其灵活的一个功能。大部分优秀的第三方组件都具有自定义模板的能力,而在 Data Grid 中,模板更是一个不可或缺的功能。Extensions Data Grid 的模板功能已经比较完善,单元格模板除了基本的方法外,还增加了更为简单易用的方法。</p>
<h4 id="普通方法">普通方法</h4>
<pre><code class="language-html">&lt;mtx-grid ="list"
          ="columns"&gt;
&lt;/mtx-grid&gt;

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

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 },
    ];
}
}
</code></pre>
<blockquote>
<p>官网示例:Custom cell template</p>
</blockquote>
<p>引用模板实例是一种很常见的思路,但是弊端就是必须将列定义写在 ngOnInit 中,而且要先引用所用的自定义模板实例。这种写法很不灵活。</p>
<h4 id="升级方案">升级方案</h4>
<pre><code class="language-html">&lt;mtx-grid ="list"
          ="columns"
          ="{ city: cityTpl }"&gt;
&lt;/mtx-grid&gt;

&lt;ng-template #cityTpl let-row let-index="index" let-col="colDef"&gt;
&lt;button mat-raised-button color="primary"&gt;{{row.city}}&lt;/button&gt;
&lt;/ng-template&gt;
</code></pre>
<blockquote>
<p>官网示例:Custom cell template 2</p>
</blockquote>
<p>这种方法直接在组件参数中定义了模板实例 <code>="{ city: cityTpl }"</code>,其中 <code>city</code> 是列定义中的 <code>field</code>,除此之外不需要再写其它任何代码,非常简单!</p>
<p>除了单元格模板之外,还有 headerTemplate、summaryTemplate、toolbarTemplate 等,可以满足大部分的个性化需求,详情见官网示例。</p>
<h3 id="选取">选取</h3>
<p><img src="https://img2020.cnblogs.com/blog/999445/202006/999445-20200626103339835-1123168463.jpg" alt="" loading="lazy"></p>
<blockquote>
<p>官网示例:Row selectable</p>
</blockquote>
<p>表格的行选取是一个很常见的需求,用途广泛。默认开启单元格选取,可以设置 <code>="false"</code> 以关闭单元格选取。</p>
<p>通过 <code>="true"</code> 可以开启行选取。</p>
<pre><code class="language-html">&lt;mtx-grid ="list"
          ="columns"
          ="rowSelectable"
          (rowSelectionChange)="log($event)"
          (cellSelectionChange)="log($event)"&gt;
&lt;/mtx-grid&gt;
</code></pre>
<p>通过 <code>="true"</code> 可以设置多选行。这里有一个细节,按住 ctrl 并单击才可以多选,或者直接点击 checkbox 也可以。如果需要隐藏 checkbox,只需要设置 <code>="true"</code>。</p>
<p>如果初始化表格时希望默认选中某些行,则只需要定义 <code>=[...]</code>。</p>
<h4 id="不可选取">不可选取</h4>
<p><img src="https://img2020.cnblogs.com/blog/999445/202006/999445-20200626110254209-1846383215.jpg" alt="" loading="lazy"></p>
<p>设置不可选取行的方式有两种,一种是设置 checkbox 为 disabled,另一种是隐藏 checkbox。配置非常简单,只需要通过 <code>rowSelectionFormatter</code> 过滤数据即可。</p>
<pre><code class="language-html">&lt;mtx-grid ="list"
          ="columns"
          ="true"
          ="rowSelectionFormatter"&gt;
&lt;/mtx-grid&gt;
</code></pre>
<pre><code class="language-ts">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) =&gt; data.name === 'Boron',
    hideCheckbox: (data) =&gt; data.name === 'Helium',
};
}
</code></pre>
<h3 id="行展开">行展开</h3>
<p><img src="https://img2020.cnblogs.com/blog/999445/202006/999445-20200626103402308-1197363292.jpg" alt="" loading="lazy"></p>
<blockquote>
<p>官网示例:Expandable row</p>
</blockquote>
<p>行展开的实现借助了 Angular Material 表格的 <code>multiTemplateDataRows</code> 参数,实现细节很多。Data Grid 的代码如下:</p>
<p>设置 <code>expandable</code> 和 <code>expansionTemplate</code></p>
<pre><code class="language-html">&lt;mtx-grid ="list"
          ="columns"
          ="true"
          ="expansionTpl"&gt;
&lt;/mtx-grid&gt;

&lt;ng-template #expansionTpl let-row&gt;
{{row.name}}
&lt;/ng-template&gt;
</code></pre>
<p>在列定义中设置 <code>showExpand</code>, 确定在哪个列显示展开符号。</p>
<pre><code class="language-ts">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;
}
</code></pre>
<h3 id="列操作">列操作</h3>
<p><img src="https://img2020.cnblogs.com/blog/999445/202006/999445-20200626110405972-517823155.jpg" alt="" loading="lazy"></p>
<blockquote>
<p>官网示例:Column hiding &amp; moving</p>
</blockquote>
<p>列的显示隐藏以及排序是非常常见的需求,这类需求曾被产品经理折磨了无数次。目前的列操作 UI 只有菜单方式,之后还会添加侧边栏的 UI,暂时不支持列的横向拖拽。</p>
<p>列的操作完全可以移到组件之外,通过设置 columns 实现,并不一定非要用 Data Grid 集成好的功能。</p>
<h2 id="总结">总结</h2>
<p>因篇幅有限,很多 Extensions Data Grid 的功能没有详细介绍。从我遇到的需求来看,目前的 Data Grid 已经可以覆盖九成的需求了,还有很多高级功能正在开发当中,欢迎大家提出建设性意见。如果大家在使用组件的过程中遇到问题,可以在 GitHub 中提交 issues 或者进讨论群提问。</p>
<p><img src="https://img2018.cnblogs.com/blog/999445/201909/999445-20190919105319134-685276492.jpg" alt="" loading="lazy"></p>


</div>
<div id="MySignature" role="contentinfo">
    <div class="signature-main">
<p>感谢您的阅读,如果您对我的文章感兴趣,可以关注我的博客,我是叙帝利,下篇文章再见!</p>
<hr>
<p>高颜值的渐变编辑器组件,支持所有 CSS 渐变语法 https://github.com/acrodata/gradient-picker</p>
<p>一款小而美的颜色选择器组件 https://github.com/acrodata/color-picker</p>
<p>低代码平台必备轻量级 GUI 库 https://github.com/acrodata/gui</p>
<p>适用于 Angular 的 CodeMirror 6 组件 https://github.com/acrodata/code-editor</p>
<p>适用于 Angular 的水印组件(防删除,盲水印) https://github.com/acrodata/watermark</p>
<p>支持拖拽和缩放的弹窗组件 https://github.com/acrodata/rnd-dialog</p>
<p>开发低代码平台的必备拖拽库 https://github.com/ng-dnd/ng-dnd</p>
<p>基于 Angular Material 的中后台管理框架 https://github.com/ng-matero/ng-matero</p>
<p>Angular Material Extensions 扩展组件库 https://github.com/ng-matero/extensions</p>
<p>Unslider 轮播图插件纯 JS 实现 https://github.com/nzbin/unsliderjs</p>
<p>仿 Windows 照片查看器插件 https://github.com/nzbin/photoviewer</p>
<p>仿 Windows 照片查看器插件 jQuery 版 https://github.com/nzbin/magnify</p>
<p>完美替代 jQuery 的模块化 DOM 库 https://github.com/nzbin/domq</p>
<p>简化类名的轻量级 CSS 框架 https://github.com/nzbin/snack</p>
<p>与任意 UI 框架搭配使用的通用辅助类 https://github.com/nzbin/snack-helper</p>
<p>单元素纯 CSS 加载动画 https://github.com/nzbin/three-dots</p>
<p>有趣的 jQuery 卡片抽奖插件 https://github.com/nzbin/CardShow</p>
<p>悬疑科幻电影推荐 https://github.com/nzbin/movie-gallery</p>
<p>锻炼记忆力的小程序 https://github.com/nzbin/memory-stake</p>
</div><br><br>
来源:https://www.cnblogs.com/nzbin/p/13032035.html
頁: [1]
查看完整版本: 基于 Angular Material 的 Data Grid 设计实现