一 前言
上一篇 Jquery遮罩插件,想罩哪就罩哪! 結尾的預告終於來了。
近期參與了一個針對內部員工個人信息收集的系統,其中有一個需求是在填寫各個相關信息時,需要能動態的增加行當時公司有自己的解決方案 那就是用GridView 那個龐大的服務器控件,我一真就不怎么喜歡用服務器控件,於是極力說服PM用js來處理,並成功爭取到了,先說下如果用GridView來處理的缺點,
1 生成的html代碼會比較冗余,
2 每一個操作都會伴隨頁面回發,
3 每個操作都會刷新頁面,這樣的用戶體驗極差,就算用updatepanel也還是會有回發,
4 有點殺雞用牛刀的感覺
那用js來處理以上的問題就都不存在了,重點是還能把js鞏固一下(算是私心吧),由於項目的時間非常緊,而且又轉成用js處理又沒有相關的經驗,整個過程壓力還是很大的,免不了加班,但是做自己喜歡的事,就算加班我也是樂在其中
二 插件介紹
基本需求如下:
1 可以動態增加 刪除行,
2 可以對行內數據進行校驗,並支持自定義校驗提示信息
3 能取到表格中的數據
4 能綁定后台數據到表格中
當然這只是大概的需求,還有很多小的細節就不寫了,也不是本文的重點。下面詳細寫下每個功能的具體思路,當然我的肯定不是最好的思路,代碼也肯定可再優化。
前后端數據交互過程如下:
前---后
前端取數據--->隱藏域---->后端取隱藏域值解析並存DB
后---前
后端取數據---->隱藏域----> 前端取隱藏域值添加到表格中
先看下效果
2.1 增加行 刪除行
增加行
思路: 跳過第一行(也就是表頭),復制第二行的html元素追加到表中,(在IE8中好像會將復制的行內文本值也會被追加到新的行中,這時需要遍歷新的行將其清空)
代碼如下
//增加行 $.fn.tables.addRow = function (gridId) { var tbRow = $("#" + gridId).find("tr").eq(1).html(); var num = $("#" + gridId + " tr").length; $("#" + gridId).append("<tr id=" + num + ">" + tbRow + "</tr>"); $("#" + gridId).find("tr").last().find("td").last().children().removeAttr("name").attr("value", ""); };
刪除行:
思路:點刪除時通過當前元素找到其父元素"tr"然后再刪除該"tr"元素
代碼:
//刪除行 //參數obj 當前對象 $.fn.tables.deleteRow = function (obj) { var $this = $(obj); if ($this.parents("table").find("tr").length <= 2) { return false; } $this.parents("tr").remove(); };
2.2 取表格內數據:
思路:為了讓前端的數據到后端容易解析,決定用json格式,將實體的屬性名添加到元素的name屬性中,組成結果時用元素的name屬性做名稱,value屬性做值
遍歷表格中每一行,並添加到一個數組中。取到的結果格式就是這樣的 {"Relation":"Zery","RelativesName":"Zhang"},傳到后台時將其序列化成對象就可以了
<input name="RelativesName" type="text" style="width: 100px" class="required" />
代碼:

//取表格所有數據 //返回值為一個數組,數組內每一個元素對應一行內的數據; $.fn.tables.getResult = function (gridId) { var resultArray = new Array(); $("#" + gridId).find("tr").slice(1).each(function () { var trResult = "{"; $(this).find("td").each(function () { var $child = $(this).children(); if ($child.length > 1) { //td下有多個元素的(多個元素會用"|"拼接起來) //td下有多個元素的暫時禁用 //$child.each(function () { // //alert($(this).attr("type")); // switch ($(this).attr("type")) { // case "text": // //alert($child.val() + "文件框的!"); // trResult += '"' + $(this).attr("name") + '"' + ":" + '"' + $(this).val() + '"' + ","; // break; // case "select": // // alert($(this).find("option:selected").text()); // trResult += '"' + $(this).attr("name") + '"' + ":" + '"' + $(this).find("option:selected").val() + '"' + ","; // break; // case "checkbox": // if ($(this).attr("checked") === "checked") { // trResult += '"' + $(this).attr("name") + '"' + ":" + '"' + $(this).val() + '"' + ","; // } // break; // case "radio": // if ($(this).attr("checked") === "checked") { // trResult += '"' + $(this).attr("propertys") + '"' + ":" + '"' + $(this).val() + '"' + ","; // } // break; // default: // } //}); // trResult.push(valueStr); } else { //td 下只有一個元素 switch ($child.attr("type")) { case "text": trResult += '"' + $child.attr("name") + '"' + ":" + '"' + $child.val() + '"' + ","; break; case "select": trResult += '"' + $child.attr("name") + '"' + ":" + '"' + $child.find("option:selected").val() + '"' + ","; break; case "checkbox": if ($(this).attr("checked") === "checked") { trResult += '"' + $child.attr("name") + '"' + ":" + '"' + $child.val() + '"' + ","; } else { trResult += '"' + $child.attr("name") + '"' + ":0" + '"' + ","; } break; case "radio": if ($(this).attr("checked") === "checked") { trResult += '"' + $child.attr("propertys") + '"' + ":" + '"' + $child.val() + '"' + ","; } else { trResult += '"' + $child.attr("propertys") + '"' + ":0" + '"' + ","; } break; default: } } }); trResult = trResult.substring(0, trResult.length - 1); resultArray.push(trResult + "}"); }); return resultArray; };
2.3 驗證行數據:
思路:遍歷每行中的元素根據元素類型來做判空處理,如果為空則在元素后追加提示信息,如果元素需要自定義錯誤信息時可以給元素加一個errormsg屬性
代碼:

//驗證行 $.fn.tables.validateRow = function (gridId) { //是否驗證通過 var isPass = true; $("#" + gridId).find("tr").slice(1).each(function () { $(this).find("td").each(function () { if ($(this).children().hasClass('text-error')) { return isPass = false; } var $this = $(this).children(); if ($this.length > 1) { //checkbox選中數 if ($(this).find("input[type='checkbox']").length > 0) { var checkedNum = $(this).find("input[type='checkbox'][checked]").length; if (checkedNum <= 0) { //TODO:復選框應該默認有選中項目不需要做驗證 // message = "此字段必填"; //return message; } } //radio選中數 if ($(this).find("input[type='radio']").length > 0) { var radioNum = $(this).find("input[type='radio'][checked]").length; if (radioNum <= 0) { //TODO:復選框應該默認有選中項目不需要做驗證 //message = "此字段必填"; //return message; } } } else { switch ($this.attr("type")) { case "text": var errorMsg = "此字段為必填項!"; if ($this.hasClass("required")) { if ($.trim($this.val()) == "") { isPass = false; if ($this.attr("errormsg") == undefined) { $this.addClass('error').after("<span class='help-block text-error'>" + errorMsg + "</span>"); } else { errorMsg = $this.attr("errormsg"); $this.addClass('error').after("<span class='help-block text-error'>" + errorMsg + "</span>"); } } } if ($this.hasClass("detepickers")) { if ($.trim($this.val()) == "") { isPass = false; $this.addClass('error').after("<span class='help-block text-error'>" + errorMsg + "</span>"); } } break; case "select": //默認有選中項不需要做驗證 //若選中項為“請選擇”時可以根據選中值來驗證 break; default: } } }); }); return isPass; };
2.4 綁定數據
思路: 后台取到值后序列化成json后通過隱藏域傳到前端,前端根據元素的name屬性值與 Json串中的的key 做對比如果相等則賦值, 需要注意的是 所有的元素我都是用type屬性來區分的 如果該元素沒有type屬性則需要自己加上 如 select 元素 需要加上 type='select'
代碼:

//綁定數據 $.fn.tables.bindData = function (gridId, tbResult) { //返回結果為一個數組 // var result = $("#<%=hd"+gridId+".ClientID %>").val(); var jsons = $.parseJSON(tbResult); if (jsons == null) { return; } var trHtml = $("#" + gridId).find("tr").slice(1).first().html(); //遍歷行結果 for (var i = 0; i < jsons.length; i++) { var trnum = $("#" + gridId).find("tr").slice(1).length - 1; if (i > trnum) { $("#" + gridId).append("<tr>" + trHtml + "</tr>"); } //遍歷行中每一列的key for (var key in jsons[i]) { $("#" + gridId).find("tr").eq(i + 1).find("td").each(function () { var $child = $(this).children(); if ($child.attr("name") === key || $child.attr("propertys") === key) { switch ($child.attr("type")) { case "text": $child.val(jsons[i][key]); break; case "select": //可以根據value 來設置選中項目 $child.find("option").each(function () { var value = $(this).val(); if (value === jsons[i][key]) { $(this).attr("selected", true); } }); break; case "checkbox": //可以根據value 來設置選中項目 $child.each(function () { var name = $(this).attr("name"); if (name === key && $(this).val() === jsons[i][key]) { $(this).attr("checked", true); } }); case "radio": $child.each(function () { var name = $(this).attr("propertys"); if (name === key && $(this).val() === jsons[i][key]) { $(this).attr("checked", true); } }); break; case "a": case "button": $child.attr("value", jsons[i][key]); break; default: } } }); } } };
以上都只是簡單介紹思路,雖然寫出來只有一點點,但是 當時在開發時在某些點卻時想了很久,因為針對項目做了很多的特殊修改,所以這里只實現很基本的功能,代碼也僅供參考,主要還是實現的思路
三 總結
項目在幾天前已經上線了,用戶的反應還不錯,也算對得起自己的付出吧,當時承擔着項目延期的風險,決定用js來做的理由很簡單,只是想把用戶體驗做的好一點。
做完后想想,有時候我們應該對自己要求高一點,不要總活在自己的舒適區,有挑戰才能有成長。
源碼示例下載:http://files.cnblogs.com/zery/WebApplication1.rar
如果您覺得本文有給您帶來一點收獲,不妨點個推薦,為我的付出支持一下,謝謝~
如果希望在技術的道路上能有更多的朋友,那就關注下我吧,讓我們一起在技術的路上奔跑