jQuery EasyUI Datagrid组件默认视图分析

时间:2022-11-06 09:35:37

  在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. function updateTableRow(frozen) {
  195. var fields = $(target).datagrid("getColumnFields", frozen);
  196. //这个地方查找grid的数据主体表格(可能包含冻结列对应的主体表格和普通列对应的主体表格)
  197. //getTr这个函数,我在博客上介绍过,请参考:http://www.easyui.info/archives/396.html
  198. var tr = opts.finder.getTr(target, rowIndex, "body", (frozen ? 1 : 2));
  199. var checked = tr.find("div.datagrid-cell-check input[type=checkbox]").is(":checked");
  200. //这里调用了renderRow方法来重新获取当前行的html字符串
  201. tr.html(this.renderRow.call(this, target, fields, frozen, rowIndex, rows[rowIndex]));
  202. tr.attr("style", style || "");
  203. //更新的时候保留checkbox状态(包含两层信息:一是有ck列;二是ck列被之前就被选中)
  204. if(checked) {
  205. tr.find("div.datagrid-cell-check input[type=checkbox]")._propAttr("checked", true);
  206. }
  207. };
  208. //更新冻结列对应的行
  209. updateTableRow.call(this, true);
  210. //更新普通列对应的行
  211. updateTableRow.call(this, false);
  212. //重新布局表格面板
  213. $(target).datagrid("fixRowHeight", rowIndex);
  214. },
  215. insertRow: function(target, rowIndex, row) {
  216. var state = $.data(target, "datagrid");
  217. //options
  218. var opts = state.options;
  219. //document of datagrid
  220. var dc = state.dc;
  221. var data = state.data;
  222. //兼容无效的rowIndex,默认设置为在最后一行追加
  223. if(rowIndex == undefined || rowIndex == null) {
  224. rowIndex = data.rows.length;
  225. }
  226. //为啥不跟上面的条件并到一起,真是蛋疼
  227. if(rowIndex > data.rows.length) {
  228. rowIndex = data.rows.length;
  229. }
  230. /**
  231. * 下移rows
  232. * @param  {boolean} frozen 是否为frozen部分
  233. * @return {undefined}        无返回值
  234. */
  235. function moveDownRows(frozen) {
  236. //1:冻结列部分;2:普通列部分
  237. var whichBody = frozen ? 1 : 2;
  238. for(var i = data.rows.length - 1; i >= rowIndex; i--) {
  239. var tr = opts.finder.getTr(target, i, "body", whichBody);
  240. //注意这地方设置了tr的"datagrid-row-index"和"id"属性
  241. tr.attr("datagrid-row-index", i + 1);
  242. tr.attr("id", state.rowIdPrefix + "-" + whichBody + "-" + (i + 1));
  243. //计算行号
  244. if(frozen && opts.rownumbers) {
  245. //因rowIndex从0开始,以及须插入位置以下的tr要统一下移,所以新行号为i+2
  246. var rownumber = i + 2;
  247. //有分页的话,行号还要加上分页数据
  248. if(opts.pagination) {
  249. rownumber += (opts.pageNumber - 1) * opts.pageSize;
  250. }
  251. tr.find("div.datagrid-cell-rownumber").html(rownumber);
  252. }
  253. }
  254. };
  255. /**
  256. * 插入了,要插两个地方的哦(如果你是男人,你可以淫荡地笑一下)
  257. * @param  {boolean} frozen 是否是frozen部分
  258. * @return {undefined}        未返回值
  259. */
  260. function doInsert(frozen) {
  261. var whichBody = frozen ? 1 : 2;
  262. //这行代码,不知道是干嘛的,怕插入得太快而早早缴械,所以才故意拖延时间的么?
  263. var columnFields = $(target).datagrid("getColumnFields", frozen);
  264. //构造新插入行的id属性
  265. var trId = state.rowIdPrefix + "-" + whichBody + "-" + rowIndex;
  266. var tr = "<tr id=\"" + trId + "\" class=\"datagrid-row\" datagrid-row-index=\"" + rowIndex + "\"></tr>";
  267. if(rowIndex >= data.rows.length) {
  268. //如果已经有记录,则插入tr即可
  269. if(data.rows.length) {
  270. //嗯哼,getTr的这个用法不多哦,未传入行索引,第三个参数为"last",随便的意淫一下就知道是获取最后一行了
  271. //然后再在最后一行后插入一行,注意了,这里用的后入式
  272. opts.finder.getTr(target, "", "last", whichBody).after(tr);
  273. }
  274. //如果表格尚无记录,则要生成表格,同时插入tr
  275. else {
  276. var cc = frozen ? dc.body1 : dc.body2;
  277. cc.html("<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>" + tr + "</tbody></table>");
  278. }
  279. }
  280. //在rowIndex + 1前准确无误地插入,注意了,这里是前入式。
  281. else {
  282. opts.finder.getTr(target, rowIndex + 1, "body", whichBody).before(tr);
  283. }
  284. };
  285. //下移frozen部分
  286. moveDownRows.call(this, true);
  287. //下移普通列部分
  288. moveDownRows.call(this, false);
  289. //插入frozen区
  290. doInsert.call(this, true);
  291. //插入普通区
  292. doInsert.call(this, false);
  293. //总数加1
  294. data.total += 1;
  295. //维护data.rows数组,这地方是插入一个数组元素了
  296. data.rows.splice(rowIndex, 0, row);
  297. //刷新,其中包含了重新布局grid面板等复杂得一笔的操作
  298. //插入本是件很简单愉快的事情,可是你得为其后果负上沉重的代价
  299. this.refreshRow.call(this, target, rowIndex);
  300. },
  301. /**
  302. * 删除行接口
  303. * @param  {DOM object} target datagrid实例的宿主table对应的DOM对象
  304. * @param  {number} rowIndex 行索引
  305. * @return {undefined}        未返回值
  306. */
  307. deleteRow: function(target, rowIndex) {
  308. var state = $.data(target, "datagrid");
  309. var opts = state.options;
  310. var data = state.data;
  311. function moveUpRows(frozen) {
  312. var whichBody = frozen ? 1 : 2;
  313. for(var i = rowIndex + 1; i < data.rows.length; i++) {
  314. var tr = opts.finder.getTr(target, i, "body", whichBody);
  315. //"datagrid-row-index"和"id"属性减一
  316. tr.attr("datagrid-row-index", i - 1);
  317. tr.attr("id", state.rowIdPrefix + "-" + whichBody + "-" + (i - 1));
  318. if(frozen && opts.rownumbers) {
  319. var rownumber = i;
  320. if(opts.pagination) {
  321. rownumber += (opts.pageNumber - 1) * opts.pageSize;
  322. }
  323. tr.find("div.datagrid-cell-rownumber").html(rownumber);
  324. }
  325. }
  326. };
  327. //移除行
  328. opts.finder.getTr(target, rowIndex).remove();
  329. //上移frozen区
  330. moveUpRows.call(this, true);
  331. //上移普通区
  332. moveUpRows.call(this, false);
  333. //记录数减一
  334. data.total -= 1;
  335. //维护data.rows数据
  336. data.rows.splice(rowIndex, 1);
  337. },
  338. /**
  339. * 默认的onBeforeRender事件 为空
  340. * @param  {DOM object} target datagrid实例的宿主table对应的DOM对象
  341. * @param  {array} rows 要插入的数据
  342. * @return {undefined}        默认未返回值
  343. */
  344. onBeforeRender: function(target, rows) {},
  345. /**
  346. * 默认的onAfterRender 隐藏footer里的行号和check
  347. * @param  {DOM object} target datagrid实例的宿主table对应的DOM对象
  348. * @return {undefined}        未返回值
  349. */
  350. onAfterRender: function(target) {
  351. var opts = $.data(target, "datagrid").options;
  352. if(opts.showFooter) {
  353. var footer = $(target).datagrid("getPanel").find("div.datagrid-footer");
  354. footer.find("div.datagrid-cell-rownumber,div.datagrid-cell-check").css("visibility", "hidden");
  355. }
  356. }
  357. };
 
******转载:http://www.easyui.info/archives/1196.html