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 打开页面window.location和window.open的区别
Mar 17 Javascript
js控制CSS样式属性语法对照表
Dec 11 Javascript
解析Jquery取得iframe中元素的几种方法
Jul 04 Javascript
JavaScript中的splice()方法使用详解
Jun 09 Javascript
JS中产生标识符方式的演变
Jun 12 Javascript
Mvc提交表单的四种方法全程详解
Aug 10 Javascript
JavaScript简单下拉菜单特效
Sep 13 Javascript
Jquery实现跨域异步上传文件总结
Feb 03 Javascript
JS操作xml对象转换为Json对象示例
Mar 25 Javascript
Vue render渲染时间戳转时间,时间转时间戳及渲染进度条效果
Jul 27 Javascript
Vue利用History记录上一页面的数据方法实例
Nov 02 Javascript
antd Select下拉菜单动态添加option里的内容操作
Nov 02 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
一个更简单的无限级分类菜单代码
2007/01/16 PHP
在PHP里得到前天和昨天的日期的代码
2007/08/16 PHP
php之XML转数组函数的详解
2013/06/07 PHP
Laravel多域名下字段验证的方法
2019/04/04 PHP
PHP使用POP3读取邮箱接收邮件的示例代码
2020/07/08 PHP
JavaScript库 开发规则
2009/01/31 Javascript
jQuery 剧场版 你必须知道的javascript
2009/05/27 Javascript
jQuery中的常用事件总结
2009/12/27 Javascript
基于jQuery实现点击同时更改两个iframe的网址
2010/07/01 Javascript
基于jquery实现一张图片点击鼠标放大再点缩小
2013/09/29 Javascript
jquery遍历函数siblings()用法实例
2015/12/24 Javascript
JavaScript中对JSON对象的基本操作示例
2016/05/21 Javascript
jQuery弹出层插件popShow用法示例
2017/01/23 Javascript
js 数字、字符串、布尔值的转换方法(必看)
2017/04/07 Javascript
jquery+css实现下拉列表功能
2017/09/03 jQuery
基于ES6作用域和解构赋值详解
2017/11/03 Javascript
jquery如何实现点击空白处隐藏元素
2017/12/05 jQuery
微信小程序开发之tabbar图标和颜色的实现
2018/10/17 Javascript
PWA介绍及快速上手搭建一个PWA应用的方法
2019/01/27 Javascript
微信小程序基于Taro的分享图片功能实践详解
2019/07/12 Javascript
使用python实现拉钩网上的FizzBuzzWhizz问题示例
2014/05/05 Python
对python3.4 字符串转16进制的实例详解
2019/06/12 Python
python3.6+django2.0+mysql搭建网站过程详解
2019/07/24 Python
Django中使用Celery的方法步骤
2020/12/07 Python
Python 图片处理库exifread详解
2021/02/25 Python
从当地商店送来的杂货:Instacart
2018/08/19 全球购物
世界上第一个水枕头:Mediflow
2018/12/06 全球购物
天网面试题
2013/04/07 面试题
校长岗位职责
2013/11/26 职场文书
机关门卫岗位职责
2013/12/30 职场文书
过程装备与控制工程专业求职信
2014/07/02 职场文书
毕业证丢失证明范本
2014/09/20 职场文书
医院党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
村党支部群众路线教育实践活动对照检查材料
2014/09/26 职场文书
辩护意见书
2015/06/04 职场文书
JS前端可视化canvas动画原理及其推导实现
2022/08/05 Javascript