现在的位置: 首页 > easyui > Grid > datagrid > 正文
jQuery EasyUI Datagrid组件默认视图分析
2013年03月29日 datagrid ⁄ 共 15990字 暂无评论 ⁄ 被围观 13,068 views+

Datagrid基础DOM结构的一文中,我对Datagrid组件的骨架做了很详细的描述。有了骨架还并不完整,还得有血有肉有衣服穿才行。强大的Datagrid组件允许我们自己定义如何在基础骨架上长出健壮诱人的身体,我们只要定义Datagrid的视图就可以实现。

在大多数情况下,我们并无特别要求,Datagrid给我们提供了默认的视图,默认视图被使用在90%以上的场景,所以对默认视图的分析显得非常有必要。注意视图里面定义了哪些接口,哪些方法,如果要自己写视图的话,最好把这些接口和方法都写齐全。话不多说,直接上代码(部分注释比较淫荡,未成年人轻绕行)

 
  1. var view = {   
  2.     /**  
  3.      * 填充表格主体数据(生成数据部分的各行tr)  
  4.      * @param  {DOM object} target    datagrid宿主table对应的DOM对象  
  5.      * @param  {DOM object} container 数据主体容器。包含两个可能的值,即:  
  6.      * 1.frozen部分body1,对应的DOM对象为:div.datagrid-view>div.datagrid-view1>div.datagrid-body>div.datagrid-body-inner  
  7.      * 2.常规部分body2,对应的DOM对象为:div.datagrid-view>div.datagrid-view2>div.datagrid-body  
  8.      * @param  {boolean} frozen    是否是冻结列  
  9.      * @return {undefined}           未返回值  
  10.      */  
  11.     render: function(target, container, frozen) {   
  12.         var data = $.data(target, "datagrid");   
  13.         var opts = data.options;   
  14.         var rows = data.data.rows;   
  15.         var fields = $(target).datagrid("getColumnFields", frozen);   
  16.         if(frozen) {   
  17.             //如果grid不显示rownumbers并且也没有frozenColumns的话,直接退出。   
  18.             if(!(opts.rownumbers || (opts.frozenColumns && opts.frozenColumns.length))) {   
  19.                 return;   
  20.             }   
  21.         }   
  22.         //定义表格字符串,注意这里使用了数组的join方式代替了传统的"+"运算符,在大多浏览器中,这样效率会更高些。   
  23.         var html = ["<table class=\"datagrid-btable\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>"];   
  24.         for(var i = 0; i < rows.length; i++) {   
  25.             //striped属性,用于设置grid数据是否隔行变色,当然了实现原理很简单。   
  26.             var cls = (i % 2 && opts.striped) ? "class=\"datagrid-row datagrid-row-alt\"" : "class=\"datagrid-row\"";   
  27.             /**  
  28.              * 表格的rowStyler属性用于处理数据行的css样式,当然了这个样式仅仅是作用于tr标签上。  
  29.              * 这地方使用call了方法来设置上下文,如果rowStyler函数内部使用了this的话,则this指向datagrid的宿主table对应的DOM对象。  
  30.              */  
  31.             var style = opts.rowStyler ? opts.rowStyler.call(target, i, rows[i]) : "";   
  32.             var styler = style ? "style=\"" + style + "\"" : "";   
  33.             /**  
  34.              * rowId:行的唯一标示,对应于tr的id属性,其由以下几部分组成:  
  35.              * 1.字符窜常量:"datagrid-row-r";  
  36.              * 2.全局索引index:该索引值从1开始递增,同一个datagrid组件实例拥有唯一值,如果同一页面内有多个datagrid实例,那么其值从1递增分配给每个datagrid实例;  
  37.              * 3.冻结列标识frozen:该标识用于标示是否是冻结列(包含行号和用户指定的frozenColumns),"1"代表冻结列,"2"代表非冻结列;  
  38.              * 4.行数索引:该值才是真正代表“第几行”的意思,该值从0开始递增  
  39.              * 如页面内第一个datagrid实例的非冻结列第10行数据的rowId为"datagrid-row-r1-2-9"  
  40.              */  
  41.             var rowId = data.rowIdPrefix + "-" + (frozen ? 1 : 2) + "-" + i;   
  42.             html.push("<tr id=\"" + rowId + "\" datagrid-row-index=\"" + i + "\" " + cls + " " + styler + ">");   
  43.             /**  
  44.              * 调用renderRow方法,生成行数据(行内的各列数据)。  
  45.              * 这里的this就是opts.view,之所以用call方法,只是为了传参进去。这里我们使用this.renderRow(target,fields,frozen,i,rows[i])来调用renderRow方法应该也是可以的。  
  46.              */  
  47.             html.push(this.renderRow.call(this, target, fields, frozen, i, rows[i]));   
  48.             html.push("</tr>");   
  49.         }   
  50.         html.push("</tbody></table>");   
  51.         //用join方法完成字符创拼接后直接innerHTML到容器内。   
  52.         $(container).html(html.join(""));   
  53.     },   
  54.     /**  
  55.      * [renderFooter   description]  
  56.      * @param  {DOM object} target   datagrid宿主table对应的DOM对象  
  57.      * @param  {DOM object} container 可能为dc.footer1或者dc.footer2  
  58.      * @param  {boolean} frozen    是否为frozen区  
  59.      * @return {undefined}           未返回值  
  60.      */  
  61.     renderFooter: function(target, container, frozen) {   
  62.         var opts = $.data(target, "datagrid").options;   
  63.         //获取footer数据   
  64.         var rows = $.data(target, "datagrid").footer || [];   
  65.         var columnsFields = $(target).datagrid("getColumnFields", frozen);   
  66.         //生成footer区的table   
  67.         var footerTable = ["<table class=\"datagrid-ftable\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>"];   
  68.         for(var i = 0; i < rows.length; i++) {   
  69.             footerTable.push("<tr class=\"datagrid-row\" datagrid-row-index=\"" + i + "\">");   
  70.             footerTable.push(this.renderRow.call(this, target, columnsFields, frozen, i, rows[i]));   
  71.             footerTable.push("</tr>");   
  72.         }   
  73.         footerTable.push("</tbody></table>");   
  74.         $(container).html(footerTable.join(""));   
  75.     },   
  76.     /**  
  77.      * 生成某一行数据  
  78.      * @param  {DOM object} target   datagrid宿主table对应的DOM对象  
  79.      * @param  {array} fields   datagrid的字段列表  
  80.      * @param  {boolean} frozen   是否为冻结列  
  81.      * @param  {number} rowIndex 行索引(从0开始)  
  82.      * @param  {json object} rowData  某一行的数据  
  83.      * @return {string}          单元格的拼接字符串  
  84.      */  
  85.     renderRow: function(target, fields, frozen, rowIndex, rowData) {   
  86.         var opts = $.data(target, "datagrid").options;   
  87.         //用于拼接字符串的数组   
  88.         var cc = [];   
  89.         if(frozen && opts.rownumbers) {   
  90.             //rowIndex从0开始,而行号显示的时候是从1开始,所以这里要加1.   
  91.             var rowNumber = rowIndex + 1;   
  92.             //如果分页的话,根据页码和每页记录数重新设置行号   
  93.             if(opts.pagination) {   
  94.                 rowNumber += (opts.pageNumber - 1) * opts.pageSize;   
  95.             }   
  96.             /**  
  97.              * 先拼接行号列  
  98.              * 注意DOM特征,用zenCoding可表达为"td.datagrid-td-rownumber>div.datagrid-cell-rownumber"  
  99.              */  
  100.             cc.push("<td class=\"datagrid-td-rownumber\"><div class=\"datagrid-cell-rownumber\">" + rowNumber + "</div></td>");   
  101.         }   
  102.         for(var i = 0; i < fields.length; i++) {   
  103.             var field = fields[i];   
  104.             var col = $(target).datagrid("getColumnOption", field);   
  105.             if(col) {   
  106.                 var value = rowData[field];   
  107.                 //获取用户定义的单元格样式,入参包括:单元格值,当前行数据,当前行索引(从0开始)   
  108.                 var style = col.styler ? (col.styler(value, rowData, rowIndex) || "") : "";   
  109.                 //如果是隐藏列直接设置display为none,否则设置为用户想要的样式   
  110.                 var styler = col.hidden ? "style=\"display:none;" + style + "\"" : (style ? "style=\"" + style + "\"" : "");   
  111.                 cc.push("<td field=\"" + field + "\" " + styler + ">");   
  112.                 //如果当前列是datagrid组件保留的ck列时,则忽略掉用户定义的样式,即styler属性对datagrid自带的ck列是不起作用的。   
  113.                 if(col.checkbox) {   
  114.                     var styler = "";   
  115.                 } else {   
  116.                     var styler = "";   
  117.                     //设置文字对齐属性   
  118.                     if(col.align) {   
  119.                         styler += "text-align:" + col.align + ";";   
  120.                     }   
  121.                     //设置文字超出td宽时是否自动换行(设置为自动换行的话会撑高单元格)   
  122.                     if(!opts.nowrap) {   
  123.                         styler += "white-space:normal;height:auto;";   
  124.                     } else {   
  125.                         /**  
  126.                          * 并不是nowrap属性为true单元格就肯定不会被撑高,这还得看autoRowHeight属性的脸色  
  127.                          * 当autoRowHeight属性为true的时候单元格的高度是根据单元格内容而定的,这种情况主要是用于表格里展示图片等媒体。  
  128.                          */  
  129.                         if(opts.autoRowHeight) {   
  130.                             styler += "height:auto;";   
  131.                         }   
  132.                     }   
  133.                 }   
  134.                 //这个地方要特别注意,前面所拼接的styler属性并不是作用于td标签上,而是作用于td下的div标签上。   
  135.                 cc.push("<div style=\"" + styler + "\" ");   
  136.                 //如果是ck列,增加"datagrid-cell-check"样式类   
  137.                 if(col.checkbox) {   
  138.                     cc.push("class=\"datagrid-cell-check ");   
  139.                 }   
  140.                 //如果是普通列,增加"datagrid-cell-check"样式类   
  141.                 else {   
  142.                     cc.push("class=\"datagrid-cell " + col.cellClass);   
  143.                 }   
  144.                 cc.push("\">");   
  145.                 /**  
  146.                  * ck列光设置class是不够的,当突然还得append一个input进去才是真正的checkbox。此处未设置input的id,只设置了name属性。  
  147.                  * 我们注意到formatter属性对datagird自带的ck列同样不起作用。  
  148.                  */  
  149.                 if(col.checkbox) {   
  150.                     cc.push("<input type=\"checkbox\" name=\"" + field + "\" value=\"" + (value != undefined ? value : "") + "\"/>");   
  151.                 }   
  152.                 //普通列   
  153.                 else {   
  154.                     /**  
  155.                      * 如果单元格有formatter,则将formatter后生成的DOM放到td>div里面  
  156.                      * 换句话说,td>div就是如来佛祖的五指山,而formatter只是孙猴子而已,猴子再怎么变化翻跟头,始终在佛祖手里。  
  157.                      */  
  158.                     if(col.formatter) {   
  159.                         cc.push(col.formatter(value, rowData, rowIndex));   
  160.                     }   
  161.                     //操,这是最简单的简况了,将值直接放到td>div里面。   
  162.                     else {   
  163.                         cc.push(value);   
  164.                     }   
  165.                 }   
  166.                 cc.push("</div>");   
  167.                 cc.push("</td>");   
  168.             }   
  169.         }   
  170.         //返回单元格字符串,注意这个函数内部并未把字符串放到文档流中。   
  171.         return cc.join("");   
  172.     },   
  173.     /**  
  174.      * 刷新行数据,只有一个行索引(从0开始),调用的updateRow方法,这里直接跳过。  
  175.      * @param  {DOM object} target   datagrid实例的宿主table对应的DOM对象  
  176.      * @param  {number} rowIndex 行索引(从0开始)  
  177.      * @return {undefined}          未返回数据  
  178.      */  
  179.     refreshRow: function(target, rowIndex) {   
  180.         this.updateRow.call(this, target, rowIndex, {});   
  181.     },   
  182.     /**  
  183.      * 刷新行数据,该接口方法肩负着同步行高,重新计算和布局grid面板等重任  
  184.      * @param  {DOM object} target   datagrid实例的宿主table对应的DOM对象  
  185.      * @param  {number} rowIndex 行索引(从0开始)  
  186.      * @param  {json object} 行数据  
  187.      * @return {undefined}          未返回数据  
  188.      */  
  189.     updateRow: function(target, rowIndex, row) {   
  190.         var opts = $.data(target, "datagrid").options;   
  191.         var rows = $(target).datagrid("getRows");   
  192.         $.extend(rows[rowIndex], row);   
  193.         var style = opts.rowStyler ? opts.rowStyler.call(target, rowIndex, rows[rowIndex]) : "";   
  194.   
  195.         function updateTableRow(frozen) {   
  196.             var fields = $(target).datagrid("getColumnFields", frozen);   
  197.             //这个地方查找grid的数据主体表格(可能包含冻结列对应的主体表格和普通列对应的主体表格)   
  198.             //getTr这个函数,我在博客上介绍过,请参考:http://www.easyui.info/archives/396.html   
  199.             var tr = opts.finder.getTr(target, rowIndex, "body", (frozen ? 1 : 2));   
  200.             var checked = tr.find("div.datagrid-cell-check input[type=checkbox]").is(":checked");   
  201.             //这里调用了renderRow方法来重新获取当前行的html字符串   
  202.             tr.html(this.renderRow.call(this, target, fields, frozen, rowIndex, rows[rowIndex]));   
  203.             tr.attr("style", style || "");   
  204.             //更新的时候保留checkbox状态(包含两层信息:一是有ck列;二是ck列被之前就被选中)   
  205.             if(checked) {   
  206.                 tr.find("div.datagrid-cell-check input[type=checkbox]")._propAttr("checked"true);   
  207.             }   
  208.         };   
  209.         //更新冻结列对应的行   
  210.         updateTableRow.call(thistrue);   
  211.         //更新普通列对应的行   
  212.         updateTableRow.call(thisfalse);   
  213.         //重新布局表格面板   
  214.         $(target).datagrid("fixRowHeight", rowIndex);   
  215.     },   
  216.     insertRow: function(target, rowIndex, row) {   
  217.         var state = $.data(target, "datagrid");   
  218.         //options   
  219.         var opts = state.options;   
  220.         //document of datagrid   
  221.         var dc = state.dc;   
  222.         var data = state.data;   
  223.         //兼容无效的rowIndex,默认设置为在最后一行追加   
  224.         if(rowIndex == undefined || rowIndex == null) {   
  225.             rowIndex = data.rows.length;   
  226.         }   
  227.         //为啥不跟上面的条件并到一起,真是蛋疼   
  228.         if(rowIndex > data.rows.length) {   
  229.             rowIndex = data.rows.length;   
  230.         }   
  231.         /**  
  232.          * 下移rows  
  233.          * @param  {boolean} frozen 是否为frozen部分  
  234.          * @return {undefined}        无返回值  
  235.          */  
  236.         function moveDownRows(frozen) {   
  237.             //1:冻结列部分;2:普通列部分   
  238.             var whichBody = frozen ? 1 : 2;   
  239.             for(var i = data.rows.length - 1; i >= rowIndex; i--) {   
  240.                 var tr = opts.finder.getTr(target, i, "body", whichBody);   
  241.                 //注意这地方设置了tr的"datagrid-row-index"和"id"属性   
  242.                 tr.attr("datagrid-row-index", i + 1);   
  243.                 tr.attr("id", state.rowIdPrefix + "-" + whichBody + "-" + (i + 1));   
  244.                 //计算行号   
  245.                 if(frozen && opts.rownumbers) {   
  246.                     //因rowIndex从0开始,以及须插入位置以下的tr要统一下移,所以新行号为i+2   
  247.                     var rownumber = i + 2;   
  248.                     //有分页的话,行号还要加上分页数据   
  249.                     if(opts.pagination) {   
  250.                         rownumber += (opts.pageNumber - 1) * opts.pageSize;   
  251.                     }   
  252.                     tr.find("div.datagrid-cell-rownumber").html(rownumber);   
  253.                 }   
  254.             }   
  255.         };   
  256.         /**  
  257.          * 插入了,要插两个地方的哦(如果你是男人,你可以淫荡地笑一下)  
  258.          * @param  {boolean} frozen 是否是frozen部分  
  259.          * @return {undefined}        未返回值  
  260.          */  
  261.         function doInsert(frozen) {   
  262.             var whichBody = frozen ? 1 : 2;   
  263.             //这行代码,不知道是干嘛的,怕插入得太快而早早缴械,所以才故意拖延时间的么?   
  264.             var columnFields = $(target).datagrid("getColumnFields", frozen);   
  265.             //构造新插入行的id属性   
  266.             var trId = state.rowIdPrefix + "-" + whichBody + "-" + rowIndex;   
  267.             var tr = "<tr id=\"" + trId + "\" class=\"datagrid-row\" datagrid-row-index=\"" + rowIndex + "\"></tr>";   
  268.             if(rowIndex >= data.rows.length) {   
  269.                 //如果已经有记录,则插入tr即可   
  270.                 if(data.rows.length) {   
  271.                     //嗯哼,getTr的这个用法不多哦,未传入行索引,第三个参数为"last",随便的意淫一下就知道是获取最后一行了   
  272.                     //然后再在最后一行后插入一行,注意了,这里用的后入式   
  273.                     opts.finder.getTr(target, """last", whichBody).after(tr);   
  274.                 }   
  275.                 //如果表格尚无记录,则要生成表格,同时插入tr   
  276.                 else {   
  277.                     var cc = frozen ? dc.body1 : dc.body2;   
  278.                     cc.html("<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>" + tr + "</tbody></table>");   
  279.                 }   
  280.             }   
  281.             //在rowIndex + 1前准确无误地插入,注意了,这里是前入式。   
  282.             else {   
  283.                 opts.finder.getTr(target, rowIndex + 1, "body", whichBody).before(tr);   
  284.             }   
  285.         };   
  286.         //下移frozen部分   
  287.         moveDownRows.call(thistrue);   
  288.         //下移普通列部分   
  289.         moveDownRows.call(thisfalse);   
  290.         //插入frozen区   
  291.         doInsert.call(thistrue);   
  292.         //插入普通区   
  293.         doInsert.call(thisfalse);   
  294.         //总数加1   
  295.         data.total += 1;   
  296.         //维护data.rows数组,这地方是插入一个数组元素了   
  297.         data.rows.splice(rowIndex, 0, row);   
  298.         //刷新,其中包含了重新布局grid面板等复杂得一笔的操作   
  299.         //插入本是件很简单愉快的事情,可是你得为其后果负上沉重的代价   
  300.         this.refreshRow.call(this, target, rowIndex);   
  301.     },   
  302.     /**  
  303.      * 删除行接口  
  304.      * @param  {DOM object} target datagrid实例的宿主table对应的DOM对象  
  305.      * @param  {number} rowIndex 行索引  
  306.      * @return {undefined}        未返回值  
  307.      */  
  308.     deleteRow: function(target, rowIndex) {   
  309.         var state = $.data(target, "datagrid");   
  310.         var opts = state.options;   
  311.         var data = state.data;   
  312.   
  313.         function moveUpRows(frozen) {   
  314.             var whichBody = frozen ? 1 : 2;   
  315.             for(var i = rowIndex + 1; i < data.rows.length; i++) {   
  316.                 var tr = opts.finder.getTr(target, i, "body", whichBody);   
  317.                 //"datagrid-row-index"和"id"属性减一   
  318.                 tr.attr("datagrid-row-index", i - 1);   
  319.                 tr.attr("id", state.rowIdPrefix + "-" + whichBody + "-" + (i - 1));   
  320.                 if(frozen && opts.rownumbers) {   
  321.                     var rownumber = i;   
  322.                     if(opts.pagination) {   
  323.                         rownumber += (opts.pageNumber - 1) * opts.pageSize;   
  324.                     }   
  325.                     tr.find("div.datagrid-cell-rownumber").html(rownumber);   
  326.                 }   
  327.             }   
  328.         };   
  329.         //移除行   
  330.         opts.finder.getTr(target, rowIndex).remove();   
  331.         //上移frozen区   
  332.         moveUpRows.call(thistrue);   
  333.         //上移普通区   
  334.         moveUpRows.call(thisfalse);   
  335.         //记录数减一   
  336.         data.total -= 1;   
  337.         //维护data.rows数据   
  338.         data.rows.splice(rowIndex, 1);   
  339.     },   
  340.     /**  
  341.      * 默认的onBeforeRender事件 为空  
  342.      * @param  {DOM object} target datagrid实例的宿主table对应的DOM对象  
  343.      * @param  {array} rows 要插入的数据  
  344.      * @return {undefined}        默认未返回值  
  345.      */  
  346.     onBeforeRender: function(target, rows) {},   
  347.     /**  
  348.      * 默认的onAfterRender 隐藏footer里的行号和check  
  349.      * @param  {DOM object} target datagrid实例的宿主table对应的DOM对象  
  350.      * @return {undefined}        未返回值  
  351.      */  
  352.     onAfterRender: function(target) {   
  353.         var opts = $.data(target, "datagrid").options;   
  354.         if(opts.showFooter) {   
  355.             var footer = $(target).datagrid("getPanel").find("div.datagrid-footer");   
  356.             footer.find("div.datagrid-cell-rownumber,div.datagrid-cell-check").css("visibility""hidden");   
  357.         }   
  358.     }   
  359. };  

给我留言

留言无头像?


×