在关系型数据库中,普通报表数据一般是按行存储。通常情况下做报表展现开发,需依赖关系型数据库,循环处理每行数据,在客户端界面展现。这样的需求一般可以使用报表控件或写点JavaScript代码就能解决。
如今,有些报表或表单,以文档的形式存储在非关系型数据库中,例如使用MongoDB数据库存储,本文的“安全生产管理”业务案例就使用了MongoDB数据库,如下图举例所示的“安全生产管理检查”表。
基于上述需求设计开发过程中,我对JavaScript编程语言的认识逐渐提高,摒弃了简单脚本语言看法,把早年写C++、Java代码的面向对象设计开发的感觉联系上了。由于本人近年很少写代码,对JavaScript语言掌握初浅,分享面向对象使用体会,方便学习交流。
JavaScript 语言是通过一种叫做 原型(prototype)的方式来实现面向对象编程的。 本文不探讨面向对象的形式和方法,只是分享一种使用方式。
回到上图安全生产管理检查表需求,以检查表序号为1的行数据为例,JSON数据转换到展现表格上对应关系如下图所示。
这一行数据,可以看成数据结构中的一棵树,遍历树得到如图所示的表格。按面向对象方式,定义此行数据为一个对象,命名为:RowData,此对象内含rowspan及多个检查数据对象(命名为:InspectData),每个InspectData对象,内含其rowspan及多行隐患检查数据对象。
定义上述对象示例代码如下所示:
//定义报表展现数据MongoDB Scheme
function InspectSchemeKey(){
this._contents;
this._troublesitem;
this._notroubles;
this._troubles;
this._score;
this.init = function (contents,troublesitem,notroubles,troubles,score){
this._contents = contents;
this._troublesitem = troublesitem;
this._notroubles = notroubles;
this._troubles = troubles;
this._score = score;
};
}
//定义检查依据,检查隐患及数据
function InspectData(subJSONobj,basiskey){
this._basis;
this._rowspan = getJSONRowNum(subJSONobj);
this.inspects = [];
this.init = function (subJSONobj,basiskey,schemekey) {
this._basis = subJSONobj[basiskey];
tmp = subJSONobj[schemekey._contents];
for (var j = 0; j<this._rowspan; j++ ){
this.inspects[j] = "<td>" + tmp[j][schemekey._troublesitem] + "</td>" + "<td>" + tmp[j][schemekey._score] + "</td>";
}
};
}
//自定义一行数据对象(含纵向合并单元格rowspan)
function RowData(seq,JSONObj){
this.seq = seq;
this.inspectitem;
this.inspects = [];
this.rowspan = getJSONRowNum(JSONObj);
this.init = function(JSONObj){
SchemeKey = new InspectSchemeKey();
SchemeKey.init("contentd","fieldtype","ok","detail","fieldcode");
this.inspectitem = JSONObj["title"];
//for(var tmp_obj in JSONObj){
var tmp_obj = "subtable" ;
if (typeof(JSONObj[tmp_obj])=="object"){
//判断运算符是否定义
if( typeof(JSONObj[tmp_obj].length) != "undefined" ){
var n = JSONObj[tmp_obj].length;
var tmp = JSONObj[tmp_obj];
for (var i = 0;i < n; i++) {
//fieldid定义检查依据
this.inspects[i] = new InspectData(tmp[i],"fieldid");
this.inspects[i].init(tmp[i],"fieldid",SchemeKey);
}
}
}
//}
};
}
注:函数getJSONRowNum( )源码见博文《使用JavaScript编程分析多级嵌套JSON文档数据》
完成上述代码后,发现没有返回报表中整行数据对象的HTML字符串,为此,使用prototype为RowData对象添加新方法getHtml,用以返回可展现的HTML,整合验证代码如下:
//获取展现HTML(扩展RowData对象方法,定义在原型中)
RowData.prototype.getHtml = function () {
var htmlstr = "<tr>" ;
htmlstr = htmlstr + "<td rowspan=";
htmlstr = htmlstr + this.rowspan + ">"
htmlstr = htmlstr + this.inspectitem + "</td>" ;
for (var k=0; k < this.inspects.length ; k++ ) {
if (k > 0 )
htmlstr = htmlstr + "<tr>";
htmlstr = htmlstr + "<td rowspan=";
htmlstr = htmlstr + this.inspects[k]._rowspan + ">"
htmlstr = htmlstr + this.inspects[k]._basis + "</td>" ;
for (var li=0; li < this.inspects[k].inspects.length ; li++){
if (li > 0 )
htmlstr = htmlstr + "<tr>";
htmlstr = htmlstr + this.inspects[k].inspects[li];
htmlstr = htmlstr + "</tr>";
}
}
return htmlstr;
}
//验证模拟数据,alert出html。
function showAllElem(){
var jsons = ""; //定义返回JSON数据字符串
jsons = "{\"name\":\"安全生产检查\",\"title\":\"安全生产组织保障\",";
jsons = jsons + "\"subtable\":[{\"fieldid\":\"机房安全防火\",\"contentd\":[{\"fieldtype\":\"电源\",\"fieldcode\":\"分值\"}";
jsons = jsons + ",{\"fieldtype\":\"计算机\",\"fieldcode\":\"分值\"}";
jsons = jsons + ",{\"fieldtype\":\"空调\",\"fieldcode\":\"分值\"}";
jsons = jsons + ",{\"fieldtype\":\"电视\",\"fieldcode\":\"分值\"}]}";
jsons = jsons + ",{\"fieldid\":\"车辆安全\",\"contentd\":[{\"fieldtype\":\"年限\",\"fieldcode\":\"分值\"}";
jsons = jsons + ",{\"fieldtype\":\"保险\",\"fieldcode\":\"分值\"}]}";
jsons = jsons + ",{\"fieldid\":\"其他安全\",\"contentd\":[{\"fieldtype\":\"年限\",\"fieldcode\":\"分值\"}]}]";
jsons = jsons + ",\"field\":\"等分\"}";
//将 JavaScript 对象表示法 (JSON) 字符串转换为对象。
json_obj = JSON.parse(jsons);
var strtmp = new String("对象:");
var myRowdata = new RowData(1,json_obj);
myRowdata.init(json_obj);
strtmp = strtmp + myRowdata.getHtml();
alert(strtmp);
}
把showAllElem()函数加载到button上,如下代码所示“测试展示表单数据”。
<button id="test" type="button" onclick="showAllElem()">测试展示表单数据</button>
把返回字符串,嵌入如下HTML文档中:
<!DOCTYPE html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<head></head>
<body>
<table width="713" height="318" border="1" cellpadding="0" cellspacing="0">
//这里写返回HTML字符片
</table>
</body>
</html>
把执行showAllElem函数所返回html字符串截取放入上文HTML文档中,显示效果如下:
小结:使用JavaScript面向对象编程,思路清晰,代码简洁。特别是prototype的使用,为对象动态添加方法的方法,很灵活。不过,在调试程序过程中,发现自定义嵌套对象中的重名变量,如果未使用var定义,将成为全局的变量,稍后将深入学习闭包的知识。
由于作者水平有限,存在不足之处,欢迎反馈交流。
参考:
《全面理解面向对象的 JavaScript》曾滢著 2013.4
《文档型信息交互设计及相关技术实现》肖永威 2015.6
《嵌套JSON数据自动回写HTML网页》 肖永威 2015.5