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 20 Javascript
jQuery Mobile页面跳转后未加载外部JS原因分析及解决
Mar 18 Javascript
JavaScript中的prototype和constructor简明总结
Apr 05 Javascript
IE 下Enter提交表单存在重复提交问题的解决方法
May 04 Javascript
超棒的响应式布局jQuery插件Freetile.js
Nov 17 Javascript
jQuery实现网页顶部固定导航效果代码
Dec 24 Javascript
JavaScript中函数声明与函数表达式的区别详解
Aug 18 Javascript
Bootstrap中点击按钮后变灰并显示加载中实例代码
Sep 23 Javascript
vue实现点击选中,其他的不选中方法
Sep 05 Javascript
d3.js实现图形拖拽
Dec 19 Javascript
JavaScript 几种循环方式以及模块化的总结
Sep 03 Javascript
vue使用vant中的checkbox实现全选功能
Nov 17 Vue.js
移动端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
关于拼配咖啡,你要知道
2021/03/03 咖啡文化
有关PHP性能优化的介绍
2013/06/20 PHP
CakePHP框架Session设置方法分析
2017/02/23 PHP
PHP并发场景的三种解决方案代码实例
2021/02/27 PHP
js nextSibling属性和previousSibling属性概述及使用注意
2013/02/16 Javascript
用JS做的简单的可折叠的两级树形菜单
2013/09/21 Javascript
模拟用户点击弹出新页面不会被浏览器拦截
2014/04/08 Javascript
jQuery实现按钮点击遮罩加载及处理完后恢复的效果
2016/06/07 Javascript
jQuery使用serialize()表单序列化时出现中文乱码问题的解决办法
2016/07/27 Javascript
解析vue中的$mount
2017/12/21 Javascript
微信小程序实现默认第一个选中变色效果
2018/07/17 Javascript
jQuery滑动效果实现方法分析
2018/09/05 jQuery
微信小程序学习笔记之表单提交与PHP后台数据交互处理图文详解
2019/03/28 Javascript
vue slot与传参实例代码讲解
2019/04/28 Javascript
Vue.js 中的实用工具方法【推荐】
2019/07/04 Javascript
Vue+Node实现商品列表的分页、排序、筛选,添加购物车功能详解
2019/12/07 Javascript
[03:08]Ti4观战指南上
2014/07/07 DOTA
对python requests发送json格式数据的实例详解
2018/12/19 Python
python如何实现数据的线性拟合
2019/07/19 Python
python 表格打印代码实例解析
2019/10/12 Python
pycharm 关掉syntax检查操作
2020/06/09 Python
利用Storage Event实现页面间通信的示例代码
2018/07/26 HTML / CSS
德国PC硬件网站:CASEKING
2016/10/20 全球购物
Europcar美国/加拿大:预订汽车或卡车租赁服务
2018/11/13 全球购物
J2EE中的容器都包括哪些
2013/08/21 面试题
新年寄语大全
2014/04/12 职场文书
写求职信有哪些注意事项
2014/05/08 职场文书
管理工程专业求职信
2014/08/10 职场文书
学校法制宣传日活动总结
2014/11/01 职场文书
团日活动总结格式
2015/05/11 职场文书
教育读书笔记
2015/07/02 职场文书
小学班主任培训心得体会
2016/01/07 职场文书
Python爬虫之自动爬取某车之家各车销售数据
2021/06/02 Python
python playwright 自动等待和断言详解
2021/11/27 Python
Python Pandas读取Excel日期数据的异常处理方法
2022/02/28 Python
mysql的Buffer Pool存储及原理
2022/04/02 MySQL