概述
本 Demo 展示了如何使用 GeneralSlicerData 创建自定义切片器,实现多维度数据的联动筛选。Demo 中创建了两个自定义切片器,分别筛选"城市"和"性别"列,当其中一个切片器执行筛选时,另一个切片器会自动更新显示符合条件的选项,表格也会同步显示筛选结果。
实现思路
创建数据源,包含姓名、性别、城市和生日四列数据
初始化表格,将数据显示在页面上
创建 GeneralSlicerData 实例,传入二维数组和列名
定义 CustomSlicer 自定义切片器类,实现切片器的核心方法
创建两个切片器实例,分别绑定到"城市"和"性别"列
在切片器中构建复选框 UI,响应选择事件
重写 onFiltered 方法,根据筛选结果更新表格显示
代码解析
创建 GeneralSlicerData 实例
这段代码创建了一个通用切片器数据实例。data 参数是二维数组,包含所有行数据;columnNames 参数是列名数组。GeneralSlicerData 会处理这些数据并提供筛选功能。
定义自定义切片器
自定义切片器通过 setData 方法绑定到 GeneralSlicerData。关键步骤是调用 attachListener(this) 将切片器附加为监听器,这样当数据筛选时会自动触发切片器的 onFiltered 方法。
构建切片器 UI
使用 getExclusiveData(columnName) 获取指定列的唯一值(去重后的数据),然后为每个值创建一个复选框。初始化时所有复选框都是选中状态。
响应筛选事件
当复选框状态改变时,收集所有选中项在 exclusiveData 中的索引,然后调用 doFilter 方法执行筛选。如果没有任何选中项,则调用 doUnfilter 清除筛选。exclusiveRowIndexes 参数指定要保留的数据行索引。
更新表格显示
onFiltered 方法在数据筛选后自动调用。使用 getFilteredRowIndexes() 获取筛选后保留的行索引,然后遍历表格行,隐藏不在筛选结果中的行。
运行效果
页面左侧显示完整的数据表格,包含 9 条人员信息
右侧显示两个切片器面板:城市筛选器和性别筛选器
点击城市切片器中的复选框,表格会立即显示该城市的人员,性别切片器也会自动更新选项状态
点击性别切片器中的复选框,表格会筛选显示对应性别的人员,城市切片器同步更新
可以同时使用两个切片器进行组合筛选,例如筛选"纽约"的"女性"人员
取消所有复选框选择会显示所有数据
API 参考
GeneralSlicerData 构造函数
data: 二维数组,包含所有行数据
columnNames: 字符串数组,指定每列的名称
getExclusiveData 方法
columnName: 列名
返回值: 该列的唯一值数组(去重)
doFilter 方法
columnName: 要筛选的列名
exclusiveRowIndexes: 要保留的数据索引数组
doUnfilter 方法
columnName: 要取消筛选的列名
getFilteredRowIndexes 方法
返回值: 筛选后保留的行索引数组
attachListener 方法
listener: 监听器对象,需要实现 onFiltered 方法
onFiltered 方法
切片器监听器需要实现的方法,在数据筛选后自动调用:
window.onload = function () {
// Define data source.
var columnNames = ["Name", "Sex", "City", "Birthday"],
data = [
["Bob", "Man", "NewYork", "1968/06/08"],
["Betty", "Woman", "Washington", "1972/07/03"],
["Alice", "Woman", "Atlanta", "1964/03/02"],
["Tom", "Man", "Houston", "1986/12/03"],
["Jenny", "Woman", "Washington", "1956/10/13"],
["Nacy", "Woman", "NewYork", "1989/01/14"],
["John", "Man", "Houston", "1995/01/01"],
["Mark", "Man", "Atlanta", "1965/11/11"],
["Susan", "Woman", "Atlanta", "1983/07/08"]];
// Build data UI.
initFilteredResultList(columnNames, data);
// Create GeneralSlicerData.
var slicerData = new GC.Spread.Slicers.GeneralSlicerData(data, columnNames);
// Create a custom slicer and attach it to dom tree.
var slicer1 = new CustomSlicer(document.getElementById('cityContainer'));
slicer1.setData(slicerData, 'City');
var slicer2 = new CustomSlicer(document.getElementById('sexContainer'));
slicer2.setData(slicerData, 'Sex');
};
// Define custom slicer.
function CustomSlicer(container) {
this.container = container;
this.slicerData = null;
this.columnName = null;
}
CustomSlicer.prototype.setData = function (slicerData, columnName) {
this.slicerData = slicerData;
this.columnName = columnName;
// Invoke attachListener method.
this.slicerData.attachListener(this);
this.onDataLoaded();
}
CustomSlicer.prototype.onDataLoaded = function () {
var columnName = this.columnName,
exclusiveData = this.slicerData.getExclusiveData(columnName);
var domString = '<span>' + this.columnName + ':</span>' + '<br />';
exclusiveData.forEach(function (exclusiveDataItem, index) {
var id = columnName + index + 1;
domString += '<input type="checkbox" class="' + columnName + '" value="' + exclusiveDataItem + '" id="' + id + '" style="margin-left:10px;" checked>'
+ '<label for="' + id + '">' + exclusiveDataItem + '</label>'
+ '<br />';
});
this.container.innerHTML = domString;
var self = this;
var elements = document.getElementsByClassName(columnName);
for (var _index = 0; _index < elements.length; _index++) {
var element = elements[_index]
element.onchange = function (e) {
var parent = e.target.parentNode,
items = parent.childNodes,
indexes = [];
for (var i = 0, length = items.length; i < length; i++) {
if (items[i].checked) {
var value = items[i].value;
if (!isNaN(parseInt(value))) {
value = parseInt(value);
}
indexes.push(exclusiveData.indexOf(value))
}
}
if (indexes.length === 0) {
// Invoke doUnfilter method when all item are not selected.
self.slicerData.doUnfilter(self.columnName);
}
else {
// Invoke doFilter method when any item is selected.
self.slicerData.doFilter(self.columnName, {exclusiveRowIndexes: indexes});
}
}
}
};
// OverWrite onFiltered method to filter your data.
CustomSlicer.prototype.onFiltered = function () {
var slicerdata = this.slicerData;
var filteredRowIndexs = slicerdata.getFilteredRowIndexes();
var trs = document.getElementsByTagName('tr');
for (var i = 0; i < slicerdata.data.length; i++) {
if (filteredRowIndexs.indexOf(i) !== -1) {
trs[i + 1].style.display = '';
} else {
trs[i + 1].style.display = 'none';
}
}
}
function initFilteredResultList(columnNames, data) {
var tableStr = '';
for (var i = 0; i < columnNames.length; i++) {
tableStr += "<th>" + columnNames[i] + "</th>";
}
for (var i = 0; i < data.length; i++) {
tableStr += "<tr>";
for (var j = 0; j < data[i].length; j++) {
tableStr += "<td>" + data[i][j] + "</td>";
}
tableStr += "</tr>";
}
var table = document.createElement('table');
table.border = '1';
table.cellPadding = '0';
table.cellSpacing = '0';
table.innerHTML = tableStr;
document.getElementById('ss').appendChild(table);
}
<!doctype html>
<html style="height:100%;font-size:14px;">
<head>
<meta name="spreadjs culture" content="zh-cn" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css"
href="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/dist/gc.spread.sheets.all.min.js"
type="text/javascript"></script>
<script
src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-resources-zh/dist/gc.spread.sheets.resources.zh.min.js"
type="text/javascript"></script>
<script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div class="sample-tutorial">
<div id="ss" class="sample-spreadsheets"></div>
<div class="options-container">
<div id="info">使用左侧表格中的 GeneralSlicerData 按“城市”和“性别”列筛选数据。</div>
<p>
<div id="info">使用下方切片器按第三列筛选数据:</div>
</p>
<div id="cityContainer"></div>
<p>
<div id="info">使用下方切片器按第二列筛选数据:</div>
</p>
<div id="sexContainer"></div>
</div>
</div>
</body>
</html>
.sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
.sample-spreadsheets {
width: calc(100% - 280px);
height: 100%;
overflow: auto;
float: left;
}
.options-container {
float: right;
width: 280px;
padding: 12px;
height: 100%;
box-sizing: border-box;
background: #fbfbfb;
overflow: auto;
}
label {
display: inline-block;
min-width: 90px;
margin: 6px 0;
}
hr {
border-color: #fff;
opacity: .2;
margin: 12px 0;
}
table th,
table td {
padding: 4px 8px;
}
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}