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 相关文章推荐
javascript同步Import,同步调用外部js的方法
Jul 08 Javascript
jQuery textarea的长度进行验证
May 06 Javascript
jquery parent和parents的区别分析
Oct 02 Javascript
js获取select默认选中的Option并不是当前选中值
May 07 Javascript
深入剖析JavaScript中的函数currying柯里化
Apr 29 Javascript
JS代码实现table数据分页效果
May 26 Javascript
js html css实现复选框全选与反选
Oct 09 Javascript
H5移动端适配 Flexible方案
Oct 24 Javascript
vue中引入mxGraph的步骤详解
May 17 Javascript
vue实现拖拽的简单案例 不超出可视区域
Jul 25 Javascript
js实现贪吃蛇小游戏(加墙)
Jul 31 Javascript
在Vue中使用Echarts实例图的方法实例
Oct 10 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
web server使用php生成web页面的三种方法总结
2013/10/28 PHP
PHP用mb_string函数库处理与windows相关中文字符及Win环境下开启PHP Mb_String方法
2015/11/11 PHP
PHP文件上传问题汇总(文件大小检测、大文件上传处理)
2015/12/24 PHP
javascript+dom树型菜单类,希望朋友们一起进步
2007/05/03 Javascript
用javascript实现的激活输入框后隐藏初始内容
2007/06/29 Javascript
一些Javascript的IE和Firefox(火狐)兼容性的问题总结及常用例子
2009/05/21 Javascript
JavaScript 弹出窗体点击按钮返回选择数据的实现
2010/04/01 Javascript
让ie运行js时提示允许阻止内容运行的解决方法
2010/10/24 Javascript
父子窗体间传递JSON格式的数据的代码
2010/12/25 Javascript
js字符串截取函数substr substring slice使用对比
2013/11/27 Javascript
JS中的==运算: [''] == false —&gt;true
2016/07/24 Javascript
AngularJS 执行流程详细介绍
2016/08/18 Javascript
ES6概念 ymbol.for()方法
2016/12/25 Javascript
原生JS实现左右箭头选择日期实例代码
2017/03/14 Javascript
js上传图片预览的实现方法
2017/05/09 Javascript
判断滚动条滑到底部触发事件(实例讲解)
2017/11/15 Javascript
nodejs基于mssql模块连接sqlserver数据库的简单封装操作示例
2018/01/05 NodeJs
利用Electron简单撸一个Markdown编辑器的方法
2019/06/10 Javascript
JS typeof fn === 'function' &amp;&amp; fn()详解
2020/08/22 Javascript
pandas中的DataFrame按指定顺序输出所有列的方法
2018/04/10 Python
TensorFlow实现简单卷积神经网络
2018/05/24 Python
Python和Go语言的区别总结
2019/02/20 Python
​如何愉快地迁移到 Python 3
2019/04/28 Python
Windows平台Python编程必会模块之pywin32介绍
2019/10/01 Python
Jupyter打开图形界面并画出正弦函数图像实例
2020/04/24 Python
python 数据类型强制转换的总结
2021/01/25 Python
美国羽绒床上用品第一品牌:Pacific Coast
2018/08/25 全球购物
Eton丹麦官网:精美的男式衬衫
2020/05/27 全球购物
GWebs公司笔试题
2012/05/04 面试题
小学毕业感言150字
2014/02/05 职场文书
班级学习计划书
2014/04/27 职场文书
2014年工程部工作总结
2014/11/25 职场文书
Python破解极验滑动验证码详细步骤
2021/05/21 Python
Python趣味实战之手把手教你实现举牌小人生成器
2021/06/07 Python
MySQL系列之十五 MySQL常用配置和性能压力测试
2021/07/02 MySQL
Zabbix6通过ODBC方式监控Oracle 19C的详细过程
2022/09/23 Servers