xmlplus组件设计系列之网格(DataGrid)(10)


Posted in Javascript onMay 05, 2017

这一章我们要实现是一个网格组件,该组件除了最基本的数据展示功能外,还提供排序以及数据过滤功能。

xmlplus组件设计系列之网格(DataGrid)(10)

数据源

为了测试我们即将编写好网格组件,我们采用如下格式的数据源。此数据源包含两部分的内容,分别是表头数据集和表体数据集。网格组件实例最终的列数由表头数据集的长度决定。

var data = {
 gridColumns: ['name', 'power'],
 gridData: [
 { name: 'Chuck Norris', power: Infinity },
 { name: 'Bruce Lee', power: 9000 },
 { name: 'Jackie Chan', power: 7000 },
 { name: 'Jet Li', power: 8000 }
 ]
};

顶层设计

从视觉上,我们很自然地把网格组件划分为表头与表体。此网格组件有三个功能,所以应该提供三个动态接口。但我们注意到排序功能是通过点击表头进行的,而表头属于网格组件的一部分,所以该功能应该内置。从而,实际上我们的网格组件对外只暴露两个动态接口:一个用于过滤,另一个用于接收数据源。所以我们可以得到如下的一个顶层设计。

DataGrid: {
 xml: `<table id='table'>
  <Thead id='thead'/>
  <Tbody id='tbody'/>
  </table>`,
 fun: function (sys, items, opts) {
 function setValue(data) {
  items.thead.val(data.gridColumns);
  items.tbody.val(data.gridColumns, data.gridData);
 }
 function filter(filterKey) {
  // 过滤函数
 }
 return { val: setValue, filter: filter };
 }
}

设计表头

表头只有一行,所以可以直接给它提供一个 tr 元素。tr 元素的子级项 th 的个数取决于表头数据集的长度,所以需要动态创建。由于 th 元素包含了排序功能,所以需要另行封装。下面是我们给出的表头的设计。

Thead: {
 xml: `<thead id='thead'>
  <tr id='tr'/>
  </thead>`,
 fun: function (sys, items, opts) {
 function setValue(value) {
  sys.tr.children().call("remove");
  data.forEach(item => sys.tr.append("Th").value().val(item));
 }
 return { val: setValue };
 }
}

表头数据项组件提供一个文本设置接口。该组件本身并不负责排序,它只完成自身视图状态的变更以及排序命令的派发。排序命令的派发需要携带两个数据:一个是排序关键字,也就是表头文本;另一个排序方向,升或者降。

Th: {
 css: "#active { color: #fff; } #active #arrow { opacity: 1; } #active #key { color: #fff; }\
  #arrow { display: inline-block; vertical-align: middle; width: 0; height: 0; margin-left: 5px; opacity: 0.66; }\
  #asc { border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 4px solid #fff;}\
  #dsc { border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid #fff; }",
 xml: "<th id='th'>\
  <span id='key'/><span id='arrow'/>\
  </th>",
 fun: function (sys, items, opts) {
 var order = "#asc";
 this.watch("sort", function (e, key, order) {
  sys.key.text().toLowerCase() == key || sys.th.removeClass("#active");
 });
 this.on("click", function (e) {
  sys.th.addClass("#active");
  sys.arrow.removeClass(order);
  order = order == "#asc" ? "#dsc" : "#asc";
  sys.arrow.addClass(order).notify("sort", [sys.key.text().toLowerCase(), order]);
 });
 sys.arrow.addClass("#asc");
 return { val: sys.key.text };
 }
}

设计表体

表体可以有多行,但表体只负责展示数据,所以实现起来比表头要简单的多。

Tbody: {
 xml: `<tbody id='tbody'/>`,
 fun: function (sys, items, opts) {
 function setValue(gridColumns, gridData) {
  sys.tbody.children().call("remove");
  gridData.forEach(data => 
  tr = sys.tbody.append("tr");
  gridColumns.forEach(key => tr.append("td").text(data[key]));
  ));
 }
 return { val: setValue };
 }
}

加入排序功能

为了便于管理,我们把排序功能单独封装成一个组件,该组件提供一个排序接口,同时侦听一个排序消息。一旦接收到排序消息,则记录下关键字与排序方向,并派发一个表体刷新命令。

Sort: {
 fun: function (sys, items, opts) {
 var sortKey, sortOrder;
 this.watch("sort", function (e, key, order) {
  sortKey = key, sortOrder = order;
  this.trigger("update");
 });
 return function (data) {
  return sortKey ? data.slice().sort(function (a, b) {
  a = a[sortKey], b = b[sortKey];
  return (a === b ? 0 : a > b ? 1 : -1) * (sortOrder == "#asc" ? 1 : -1);
  }) : data;
 };
 }
}

要完整地实现排序功能,对组件 DataGrid 作一些修正,主要是内置上述的排序功能组件并侦听表体刷新指令。一旦接收到刷新指令,则对表体数据完成排序并刷新表体。

DataGrid: {
 xml: `<table id='table'>
  <Thead id='thead'/>
  <Tbody id='tbody'/>
  <Sort id='sort'/>
  </table>`,
 fun: function (sys, items, opts) {
 var data = {gridColumns: [], gridData: []};
 function setValue(value) {
  data = value;
  items.thead.val(data.gridColumns);
  items.tbody.val(data.gridColumns, data.gridData);
 }
 function filter(filterKey) {
  // 过滤函数
 }
 this.on("update", function() {
  items.tbody.val(items.sort(data.gridData));
 });
 return { val: setValue, filter: filter };
 }
}

加入过滤功能

与排序功能的加入流程类似,我们把过滤功能单独封装成一个组件,该组件提供一个过滤接口,同时侦听一个过滤消息。一旦接收到消息,则记录下过滤关键字,并派发一个表体刷新命令。

Filter: {
 fun: function (sys, items, opts) {
 var filterKey = "";
 this.watch("filter", function (e, key) {
  filterKey = key.toLowerCase();
  this.trigger("update");
 });
 return function (data) {
  return data.filter(function (row) {
  return Object.keys(row).some(function (key) {
   return String(row[key]).toLowerCase().indexOf(filterKey) > -1;
  });
  });
 };
 }
}

另外需要对组件 DataGrid 作一些修正,修正内容与上述的排序功能的加入类似,区别在于额外完善了 filter 接口以及对消息作用域进行了限制。下面是我们最终的网格组件。

DataGrid: {
 css: `#table { border: 2px solid #42b983; border-radius: 3px; background-color: #fff; }
  #table th { background-color: #42b983; color: rgba(255,255,255,0.66); cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
  #table td { background-color: #f9f9f9; }
  #table th, #table td { min-width: 120px; padding: 10px 20px; }`,
 xml: `<table id='table'>
  <Thead id='thead'/>
  <Tbody id='tbody'/>
  <Sort id='sort'/>
  <Filter id='filter'/>
  </table>`,
 map: { msgscope: true },
 fun: function (sys, items, opts) {
 var data = {gridColumns: [], gridData: []};
 function setValue(value) {
  data = value;
  items.thead.val(data.gridColumns);
  items.tbody.val(data.gridColumns, data.gridData);
 }
 function filter(filterKey) {
  sys.table.notify("filter", filterKey);
 }
 this.on("update", function() {
  items.tbody.val(items.filter(items.sort(data.gridData)));
 });
 return { val: setValue, filter: filter };
 }
}

值得注意的是这里一定要在映射项中配置限制消息作用域的选项。否则,当在一个应用中实例化多个网格组件时,消息就会互相干扰。

测试

最后我们来测试下我们完成的组件,测试的功能主要就是刚开始提到的三个:数据展示、排序以及过滤。

Index: {
 css: "#index { font-family: Helvetica Neue, Arial, sans-serif; font-size: 14px; color: #444; }\
  #search { margin: 8px 0; }",
 xml: "<div id='index'>\
  Search <input id='search'/>\
  <Table id='table'/>\
  </div>",
 fun: function (sys, items, opts) {
 items.table.val(data);
 sys.search.on("input", e => items.table.filter(sys.search.prop("value")));
 }
}

本系列文章基于 xmlplus 框架。如果你对 xmlplus 没有多少了解,可以访问 www.xmlplus.cn。这里有详尽的入门文档可供参考。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
你可能不再需要JQUERY
Mar 09 Javascript
鼠标移动到一张图片时变为另一张图片
Dec 05 Javascript
在JavaScript中监听IME键盘输入事件
May 29 Javascript
JS图片无缝滚动(简单利于使用)
Jun 17 Javascript
js中判断用户输入的值是否为空的简单实例
Dec 23 Javascript
使用jQuery判断IE浏览器版本的代码
Jun 14 Javascript
Node.js实现的简易网页抓取功能示例
Dec 05 Javascript
jQuery实现企业网站横幅焦点图切换功能实例
Apr 30 Javascript
JavaScript小技巧整理
Dec 30 Javascript
不用一句js代码初始化组件
Jan 27 Javascript
TypeScript入门-基本数据类型
Mar 28 Javascript
分分钟学会vue中vuex的应用(入门教程)
Sep 14 Javascript
移动端web滚动分页的实现方法
May 05 #Javascript
vue.js之vue-cli脚手架的搭建详解
May 05 #Javascript
Vue中使用vux的配置详解
May 05 #Javascript
Angular directive递归实现目录树结构代码实例
May 05 #Javascript
微信小程序开发图片拖拽实例详解
May 05 #Javascript
javascript 中关于array的常用方法详解
May 05 #Javascript
解决OneThink中无法异步提交kindeditor文本框中修改后的内容方法
May 05 #Javascript
You might like
PHP编码规范之注释和文件结构说明
2010/07/09 PHP
ThinkPHP实现批量删除数据的代码实例
2014/07/02 PHP
织梦sitemap地图实时推送给百度的教程
2015/08/03 PHP
thinkphp5 模型实例化获得数据对象的教程
2019/10/18 PHP
Javascript 汉字字节判断
2009/08/01 Javascript
含有CKEditor的表单如何提交
2014/01/09 Javascript
纯JS实现根据CSS的class选择DOM
2014/03/22 Javascript
js中hash和ico的关联分析
2015/02/05 Javascript
面向切面编程(AOP)的理解
2015/05/01 Javascript
jQuery遮罩层实现方法实例详解(附遮罩层插件)
2015/12/08 Javascript
Jquery操作cookie记住用户名
2016/03/29 Javascript
js验证框架实现代码分享
2016/05/18 Javascript
EditPlus 正则表达式 实战(3)
2016/12/15 Javascript
详解react如何在组件中获取路由参数
2017/06/15 Javascript
iview table render集成switch开关的实例
2018/03/14 Javascript
vue v-model动态生成详解
2018/06/30 Javascript
Vue全局分页组件的实现代码
2018/08/10 Javascript
在iFrame子页面里实现模态框的方法
2018/08/17 Javascript
在Bootstrap开发框架中使用dataTable直接录入表格行数据的方法
2018/10/25 Javascript
Vue实现固定定位图标滑动隐藏效果
2019/05/30 Javascript
vue项目查看vue版本及cli版本的实现方式
2020/10/24 Javascript
python读文件逐行处理的示例代码分享
2013/12/27 Python
Python中的赋值、浅拷贝、深拷贝介绍
2015/03/09 Python
python中的插值 scipy-interp的实现代码
2018/07/23 Python
利用Python如何批量更新服务器文件
2018/07/29 Python
Pandas DataFrame 取一行数据会得到Series的方法
2018/11/10 Python
Django实现WebSSH操作物理机或虚拟机的方法
2019/11/06 Python
总结python 三种常见的内存泄漏场景
2020/11/20 Python
PyQt5通过信号实现MVC的示例
2021/02/06 Python
波兰香水和化妆品购物网站:Notino.pl
2017/11/07 全球购物
FLIR美国官网:热成像, 夜视和红外摄像系统
2018/07/13 全球购物
校庆活动策划方案
2014/06/05 职场文书
自动化专业毕业生求职信
2014/06/18 职场文书
社区党员群众路线教育实践活动心得体会
2014/11/03 职场文书
建国大业观后感800字
2015/06/01 职场文书
详解python的内存分配机制
2021/05/10 Python