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 多浏览器兼容总结(实战经验)
Oct 30 Javascript
js获取当前路径的简单示例代码
Jan 08 Javascript
jquery实现在页面加载的时自动为日期插件添加当前日期
Aug 20 Javascript
PHP+MySQL+jQuery随意拖动层并即时保存拖动位置实例讲解
Oct 09 Javascript
jQueryUI中的datepicker使用方法详解
May 25 Javascript
JavaScript数组的栈方法与队列方法详解
May 26 Javascript
让你一句话理解闭包(简单易懂)
Jun 03 Javascript
JS转换HTML转义符的方法
Aug 24 Javascript
详解Webpack DLL用法以及功能
Jul 11 Javascript
JS实现定时任务每隔N秒请求后台setInterval定时和ajax请求问题
Oct 15 Javascript
基于vue-video-player自定义播放器的方法
Mar 21 Javascript
详解keep-alive + vuex 让缓存的页面灵活起来
Apr 19 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/05/13 PHP
解析php入库和出库
2013/06/25 PHP
PHP实现的迪科斯彻(Dijkstra)最短路径算法实例
2017/09/16 PHP
PDO::prepare讲解
2019/01/29 PHP
Firefox和IE浏览器兼容JS脚本写法小结
2008/07/07 Javascript
jquery 页面全选框实践代码
2010/04/02 Javascript
CSS+jQuery实现的一个放大缩小动画效果
2013/09/24 Javascript
原生js和jquery中有关透明度设置的相关问题
2014/01/08 Javascript
基于jquery实现的树形菜单效果代码
2015/09/06 Javascript
JS实现图片高亮展示效果实例
2015/11/24 Javascript
基于javascript实现tab选项卡切换特效调试笔记
2016/03/30 Javascript
JavaScript数组的定义及数字操作技巧
2016/06/06 Javascript
原生js实现中奖信息无间隙滚动效果
2017/01/18 Javascript
微信小程序微信支付接入开发实例详解
2017/04/12 Javascript
Vue微信项目按需授权登录策略实践思路详解
2018/05/07 Javascript
layui复选框的全选与取消实现方法
2019/09/02 Javascript
layui的表单提交以及验证和修改弹框的实例
2019/09/09 Javascript
react 不用插件实现数字滚动的效果示例
2020/04/14 Javascript
详解Nuxt内导航栏的两种实现方式
2020/04/16 Javascript
python函数返回多个值的示例方法
2013/12/04 Python
python操作ie登陆土豆网的方法
2015/05/09 Python
python列表的常用操作方法小结
2016/05/21 Python
几种实用的pythonic语法实例代码
2018/02/24 Python
Python二进制串转换为通用字符串的方法
2018/07/23 Python
Python多重继承之菱形继承的实例详解
2020/02/12 Python
自学python用什么系统好
2020/06/23 Python
详解用Python爬虫获取百度企业信用中企业基本信息
2020/07/02 Python
matplotlib更改窗口图标的方法示例
2021/02/03 Python
世界第一冲浪品牌:O’Neill
2016/08/30 全球购物
给排水专业应届生求职信
2013/10/12 职场文书
生产部厂长职位说明书
2014/03/03 职场文书
党的群众路线教育实践活动学习笔记范文
2014/11/06 职场文书
大学社团活动总结怎么写
2019/06/21 职场文书
人生哲理妙语30条:淡写流年,笑过人生
2019/09/04 职场文书
python 如何在 Matplotlib 中绘制垂直线
2021/04/02 Python
PyTorch 如何检查模型梯度是否可导
2021/06/05 Python