问题背景
在 SpreadJS 表格控件中,当Table数据绑定后,对Table插入行,新行内填充部分单元格。在getDataSource()返回值中,关于新增行的数据对象,仅有被填充单元格对应的字段。如何获取完整字段,不论对应单元格是否填充了数据。
数据绑定使用的数据是一个JavaScript对象,在代码运行期间,这个对象被创建后即存在于内存中。这也是为什么把文件导出时虽然携带了绑定的数据源,但是导入至新应用的Workbook中再调用Worksheet:getDataSource()得到的结果为null的原因。即在新应用运行时的内存空间中没有原本的数据源对象。
解决方法
回到问题背景,虽然新增行在Table区域内,但是没有数据,意味着这行数据在数据源对象中是一个各字段属性均不存在的空对象,如图1所示。
(图1. 新行对应空数据对象)
紧接着,如果对新增行内的个别单元格写入数据,数据绑定功能的双向绑定特性将单元格值和相关字段对应起来,组成一个对象。对于没被填充数据的单元格,不做类似处理。因此,Worksheet:getDataSource()获取到新增行中的数据对象仅包含被填充单元格对应的字段属性,如图2所示。
(图2. 新行数据对象包含被填充字段属性)
那如何获取完整字段呢?即,无论填充了哪些字段,都能获取到这一行对应的完整字段。Worksheet:getDataSource()应该是做不到了,因为接口并未提供可回调操作的参数,所以需要在接口外想办法了。
回顾下对Table执行数据绑定的准备工作,对Table列设置绑定路径,而每个列的绑定路径实际上对应为数据源中的一个字段,也就是说数据源模型可知。那么,在代码上下文中存储数据源模型为元数据,同时为每个字段设置数据类型和默认值,后面会用到。如图3所示,这个元数据定义了数据源对象的各个字段,名称分别为a、b、c、d、e和f,为简单起见,定义类型均为string,且默认值为空字符串。
(图3. 数据源模型元数据)
在Worksheet:getDataSource()后,结合数据源模型元数据遍历每行单元格对应的数据对象,检查当前数据对象中是否包含元数据中的每项字段。对于未包含的字段项,在当前数据对象中扩充,并且根据元数据中该字段项的数据类型和默认值设置其值,如图4所示代码片段。由于数据源对象存在于内存空间中,因此这一修改动作会直接影响数据源对象状态。
(图4. 数据对象默认值设置代码)
最终效果如下图所示:
附上Html:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>获取数据源全字段</title>
<meta name="spreadjs culture" content="zh-cn"/>
<link href="http://cdn.grapecity.com/spreadjs/hosted/css/gc.spread.sheets.excel2013white.18.1.3.css"
rel="stylesheet" type="text/css"/>
<link href="http://cdn.grapecity.com/spreadjs/hosted/css/gc.spread.sheets.designer.18.1.3.min.css" rel="stylesheet"
type="text/css"/>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/gc.spread.sheets.all.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.shapes.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.charts.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.slicers.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.print.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.barcode.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.pdf.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.pivot.pivottables.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.tablesheet.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.ganttesheet.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.report.reportsheet.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.formulapanel.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.io.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/interop/gc.spread.excelio.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/resources/zh/gc.spread.sheets.resources.zh.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/gc.spread.sheets.designer.resource.cn.18.1.3.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/gc.spread.sheets.designer.all.18.1.3.min.js"></script>
<style>
.sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
body {
position: absolute;
margin: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.gc-across-sheet-ftb {
z-index: -1 !important;
}
.gc-across-sheet-func-popup {
z-index: -1 !important;
}
</style>
</head>
<body>
<!-- <button id="readyBtn">绑定事件</button> -->
<button id="bindingBtn">绑定数据</button>
<button id="copyToBtn">插入sheet行</button>
<button id="getDataSourceBtn">获取数据源</button>
<div class="sample-tutorial">
<div class="demo-options">
<div class="option-row">
<div id="formulaBar" contenteditable="true" spellcheck="false"
style="font-family: Calibri;border: 1px solid #808080;width:100%;box-sizing: border-box;"></div>
</div>
</div>
<div id="ss" style="width:100%; height: calc(100% - 20px)"></div>
</div>
</body>
<script>
window.onload = function () {
var designer = new GC.Spread.Sheets.Designer.Designer(document.getElementById('ss'));
var spread = designer.getWorkbook();
init(spread);
}
function init(spread) {
var sheet = spread.getActiveSheet();
var table = sheet.tables.add('table1', 0, 0, 7, 6);
document.getElementById('bindingBtn').addEventListener('click', function() {
tableBinding(spread.getActiveSheet());
});
document.getElementById('getDataSourceBtn').addEventListener('click', function() {
var dataSource = spread.getActiveSheet().getDataSource();
console.log('dataSource: ', dataSource);
var metaData = {
'a': {type: 'string', defaultValue: ''},
'b': {type: 'string', defaultValue: ''},
'c': {type: 'string', defaultValue: ''},
'd': {type: 'string', defaultValue: ''},
'd': {type: 'string', defaultValue: ''},
'f': {type: 'string', defaultValue: ''}
};
setDefaultValues(dataSource.rT.tableData, metaData);
console.log('after data process: ', dataSource);
});
document.getElementById('copyToBtn').addEventListener('click', function() {
var sheet = spread.getActiveSheet();
var selection = sheet.getSelections()[0];
console.log('selection: ', selection);
sheet.addRows(selection.row, 1);
sheet.copyTo(selection.row - 1, 1, selection.row, 1, 1, 6, GC.Spread.Sheets.CopyToOptions.span);
});
}
/**
* 数据对象属性默认值设置
*/
function setDefaultValues(objArray, metaData) {
if (objArray) {
objArray.forEach(obj => {
for (const key in metaData) {
if (metaData.hasOwnProperty(key)) {
const {defaultValue} = metaData[key];
if (!obj.hasOwnProperty(key) || obj[key] == undefined) {
obj[key] = defaultValue;
}
}
}
});
}
return objArray;
}
function tableBinding(sheet) {
var table = sheet.tables.all()[0];
var tableColmn1 = new GC.Spread.Sheets.Tables.TableColumn();
tableColmn1.dataField('a');
tableColmn1.name('a字段');
var tableColmn2 = new GC.Spread.Sheets.Tables.TableColumn();
tableColmn2.dataField('c ');
tableColmn2.name('b字段');
var tableColmn3 = new GC.Spread.Sheets.Tables.TableColumn();
tableColmn3.dataField('b');
tableColmn3.name('c字段');
var tableColmn4 = new GC.Spread.Sheets.Tables.TableColumn();
tableColmn4.dataField('d');
tableColmn4.name('d字段');
var tableColmn5 = new GC.Spread.Sheets.Tables.TableColumn();
tableColmn5.dataField('e');
tableColmn5.name('e字段');
var tableColmn6 = new GC.Spread.Sheets.Tables.TableColumn();
tableColmn6.dataField('f');
tableColmn6.name('f字段');
table.bindColumns([tableColmn1, tableColmn2, tableColmn3, tableColmn4, tableColmn5, tableColmn6]);
table.bindingPath('tableData');
table.expandBoundRows(true);
var data = {
'tableData': [
{ 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6 },
{ 'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7 },
{ 'a': 3, 'b': 4, 'c': 5, 'd': 6, 'e': 7, 'f': 8 },
{ 'a': 4, 'b': 5, 'c': 6, 'd': 7, 'e': 8, 'f': 9 },
{ 'a': 5, 'b': 6, 'c': 7, 'd': 8, 'e': 9, 'f': 10 },
{ 'a': 6, 'b': 7, 'c': 8, 'd': 9, 'e': 10, 'f': 11 },
]
};
var dataSource = new GC.Spread.Sheets.Bindings.CellBindingSource(data);
sheet.setDataSource(dataSource);
}
</script>
</html>