常听人说不喜欢javascript。然而我一个一直用C#做后端的人,最喜欢的编程语言就是javascript了,我接收它的优点,也接收它的缺点!
前段时间接触过easyui,用过里面的DataGrid和Combobox等控件,感觉DataGrid的确减少了一些重复的工作,但是easyui改变原有的Element,比如textbox,不能用$("#id").val()获取值了,只能用$(“#id”).textbox('getValue')来获取,不喜欢这样。国庆放假,票到3号了,今天有时间就根据使用easyui的感觉写了两个数据控件DataGrid和Select。
先看看最后结果和前段代码:
HTML:
文章类型: <div class="input-control select"> <select id="type" data-url="/home/TypeList"></select> </div> <div class="input-control text" style="width:300px;"> <input type="text" placeholder="请输入关键字" id="keyword" /> <button class="button" onclick="search()"><span class="mif-search"></span></button> </div> <table id="tblist" class="table border bordered" data-url="/home/PageData"> <thead> <tr> <th data-field="$index"></th> <th data-field="ID">编号</th> <th data-field="Title">标题</th> <th data-field="Type" data-url="/home/TypeList">类型</th> <th data-field="Author">作者</th> <th data-field="AddTime">添加时间</th> <th data-field="_operator" data-formatter="operateFormatter">操作</th> </tr> </thead> </table>
JavaScript:
<script src="~/Scripts/jquery.min.js"></script> <script src="~/js/data-widget.js"></script> <script> var dg = new dw.DataGrid($('#tblist')); var type = new dw.Select($("#type")); function search() { dg.search({ keyword: $("#keyword").val(), type: $("#type").val() }); } function operateFormatter(value, item) { return '<a href="##" onclick="alert(' + item.ID + ')">修改</a>'; } </script>
Select数据控件:
id为type的select元素上有个data-url的属性,这个表示加载数据的地址,这个地址必须返回一个拥有ID和Name属性的对象数组的json格式,如下:
[{ID: 1, Name: "类型1"}, {ID: 2, Name: "类型2"}, {ID: 3, Name: "类型3"}, {ID: 4, Name: "类型4"},…]
在实例化dw.Select对象的时候,第一个参数是select元素的jquery对象,上面用$('type')得到,第二个是可选参数,说明如下:
{ value: undefined, // 加载过数据选中的值 onInit: undefined, // 初始化完成后执行的函数,用加载的数据作为参数 hasDefault: true, // 是否有默认选项 defaultName: '请选择', // 默认选项显示名 defaultID: '' // 默认选项值 }
DataGrid数据控件:
id为tblist的table元素可有三个自定义属性:
- data-url : 要加载数据的后台地址;
- data-page-size : 每页显示多少条数据,默认是10,会作为查询参数的pageSize;
- data-page-index : 加载时显示第几页,默认是1,会作为查询参数的pageIndex(可能永远也用不到,感觉默认值1很合理......);
data-url后台地址返回的格式应该如下:
{ PageCount : 100 //总页数 --int, PageIndex : 1 //当前页 --int, List : [ {ID:1, Title : '标题1' ,AddTime : "/Date(1332919782070)/" }, ...... ] // 分页数据列表---Array }
在实例化dw.DataGrid对象的时候,第一个参数是table元素的jquery对象,上面用$('tblist')得到,第二个是查询参数,可选。
在thead里定义表头,每个th表示一列,th可有四个自定义属性:
- data-field:返回数据列表的对象属性名,必须;
- data-formatter : 该列的格式化器,用于修改这列数据的显示形式,比如性别是bool,可以显示成“男”或“女”,比如上面例子中_operator列,虽然不对应任何字段,但是可以显示自定义的内容
- data-format : 目前用于日期格式(“/Date(1332919782070)/”)的字符串(注意是字符串类型,但是是日期格式),默认值是:“yyyy-MM-dd hh:mm”
- data-url : 用于从ID映射成对应的Name,比如文章列表的文章类型Type返回的是类型ID,可以提供这个url返回数据,控件自动映射成类型名称,从html大号字的两个地方可以看出,地址和Select的data-url是一样的。
DataGrid对象有个search方法,参数是查询参数,用法如上。
注:加红的“查询参数”是指同一个东西,这个参数会加上pageIndex和pageSize一起post到table的data-url地址来查询数据。我用asp.net mvc做的例子中的后台Action方法签名如下:
, )
注:Select的data-url和Type列的data-url相同,在内部是有缓存的,所以加载这部分数据的时候用的是同步ajax,从加载效果可以看出只加载了一次,缓存生效了:
data-widget.js依赖jQuery;
data-widget.js源码如下:
;/* file: data-widget.js author: loogn date : 2016-10-1 descript: DataGrid、Select、Pager */ var dw = (function () { var dw = {}; //根据url缓存 ,没有的话要同步获取 dw.cache = {}; dw.getCache = function (url) { var key = url.toLowerCase(); var data = dw.cache[key]; if (data) return data; $.ajax({ url: url, type: 'POST', async: false, success: function (result) { dw.cache[key] = result; data = result; } }); return data; } var styleArr = []; //pager.css styleArr.push('table .page a{ text-decoration:none; font-weight:normal;}'); styleArr.push('table .page span{ text-decoration:none; font-weight:normal;}'); styleArr.push('table .page a:hover{ text-decoration:none;}'); styleArr.push('table .page{padding: 15px 20px;text-align: left;color: #ccc;}'); styleArr.push('table .page a{display: inline-block;color: #428bca;display: inline-block;height: 25px; line-height: 25px; padding: 0 10px;border: 1px solid #ddd; margin: 0 2px;border-radius: 4px;vertical-align: middle;}'); styleArr.push('table .page span.current{display: inline-block;height: 25px;line-height: 25px;padding: 0 10px;margin: 0 2px;color: #fff;background-color: #428bca; border: 1px solid #428bca;border-radius: 4px;vertical-align: middle;}'); styleArr.push('table .page a:hover{text-decoration: none;border: 1px solid #428bca;}'); styleArr.push('table .page span.disabled{ display: inline-block;height: 25px;line-height: 25px;padding: 0 10px;margin: 0 2px; color: #bfbfbf;background: #f2f2f2;border: 1px solid #bfbfbf;border-radius: 4px;vertical-align: middle;}'); var style = $('<style>').text(styleArr.join('')); $('head').append(style); //dateFormat(new Date(),'yyyy-MM-dd hh:mm:ss') dw.dateFormat = function (date, format) { var o = { "M+": date.getMonth() + 1, //month "d+": date.getDate(), //day "h+": date.getHours(), //hour "m+": date.getMinutes(), //minute "s+": date.getSeconds(), //second "q+": Math.floor((date.getMonth() + 3) / 3), //quarter "S": date.getMilliseconds() //millisecond } if (/(y+)/.test(format)) { format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); } for (var k in o) { if (new RegExp("(" + k + ")").test(format)) { format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); } } return format; } //得到指定url参数 dw.getQueryString = function (name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", 'i'); var r = window.location.search.substr(1).match(reg); if (r !== null) return unescape(decodeURIComponent(r[2])); return null; }; //得到所有url参数对象 dw.getQueryObject = function () { var args = {}; var query = location.search.substring(1); var pairs = query.split("&"); for (var i = 0; i < pairs.length; i++) { var pos = pairs[i].indexOf('='); if (pos == -1) continue; var name = pairs[i].substring(0, pos); var value = pairs[i].substring(pos + 1); value = decodeURIComponent(value); args[name.toLowerCase()] = value; } return args; }; return dw; }()); (function (dwName, dw) { function Pager($wrap, pageCount, current, onPageChange) { this.$wrap = $wrap; this.pageCount = pageCount; this.current = current || 1; this.onPageChange = onPageChange || function () { }; this.bindEvent(); this.render(pageCount, current); } Pager.prototype.render = function (pageCount, current) { this.pageCount = pageCount; this.current = current; if (pageCount <= 1) { this.$wrap.empty(); return; } //上一页 var htmlArr = []; if (current > 1) { htmlArr.push('<a href="javascript:;" class="prevPage">上一页</a>'); } else { htmlArr.push('<span class="disabled">上一页</span>'); } //中间页码 if (current != 1 && current >= 4 && pageCount != 4) { htmlArr.push('<a href="javascript:;" class="tcdNumber">' + 1 + '</a>'); } if (current - 2 > 2 && current <= pageCount && pageCount > 5) { htmlArr.push('<span>...</span>'); } var start = current - 2, end = current + 2; if ((start > 1 && current < 4) || current == 1) { end++; } if (current > pageCount - 4 && current >= pageCount) { start--; } for (; start <= end; start++) { if (start <= pageCount && start >= 1) { if (start != current) { htmlArr.push('<a href="javascript:;" class="tcdNumber">' + start + '</a>'); } else { htmlArr.push('<span class="current">' + start + '</span>'); } } } if (current + 2 < pageCount - 1 && current >= 1 && pageCount > 5) { htmlArr.push('<span>...</span>'); } if (current != pageCount && current < pageCount - 2 && pageCount != 4) { htmlArr.push('<a href="javascript:;" class="tcdNumber">' + pageCount + '</a>'); } //下一页 if (current < pageCount) { htmlArr.push('<a href="javascript:;" class="nextPage">下一页</a>'); } else { this.$wrap.remove('.nextPage'); htmlArr.push('<span class="disabled">下一页</span>'); } //修改表现 this.$wrap.html(htmlArr.join('')); } Pager.prototype.bindEvent = function () { var pager = this; this.$wrap.on("click", "a.tcdNumber", function () { var current = parseInt($(this).text()); pager.render(pager.pageCount, current); if (typeof (pager.onPageChange) == "function") { pager.onPageChange(current); } }); //上一页 this.$wrap.on("click", "a.prevPage", function () { var current = parseInt(pager.$wrap.children("span.current").text()); pager.render(pager.pageCount, current - 1); if (typeof (pager.onPageChange) == "function") { pager.onPageChange(current); } }); //下一页 this.$wrap.on("click", "a.nextPage", function () { var current = parseInt(pager.$wrap.children("span.current").text()); pager.render(pager.pageCount, current + 1); if (typeof (pager.onPageChange) == "function") { pager.onPageChange(current); } }); } dw.Pager = Pager; }('Pager', dw)); (function (dwName, dw) { function DataGrid($table, params) { this.name = dwName; this.$table = $table; //表格对象 this.url = $table.data('url'); //加载数据的url this.pageSize = $table.data('page-size') || 10; // 每页大小 this.pageIndex = $table.data('page-index') || 1; //当前页码 this.columns = []; //列数据 this.params = {}; //查询参数 this.pager = null;//分页控件 this.isSearch = false; var $this = this; $table.find('thead>tr>th').each(function () { var $th = $(this); var field = $th.data('field'); if (field == "$index") { if (!$th.attr("style") || $th.attr("style").indexOf("width") < 0) { $th.css("width", "20px"); } } $this.columns.push({ field: field, formatter: $th.data('formatter'), format: $th.data('format'), url: $th.data('url') }); }); //分页 $table.append('<tfoot><tr><td colspan="' + this.columns.length + '" class="page"></td></tr></tfoot>'); this.load(params); } //加载数据 DataGrid.prototype.load = function (params) { params = params || {}; this.params = params; params.pageIndex = this.pageIndex; params.pageSize = this.pageSize; var $this = this; $.post(this.url, params, function (data) { $this.render(data); }); }; DataGrid.prototype.search = function (params) { this.pageIndex = 1; this.isSearch = true; this.load(params); } DataGrid.prototype.render = function (data) { var $datagrid = this; var html_arr = []; html_arr.push('<tbody>'); for (var i = 0; i < data.List.length; i++) { var item = data.List[i]; html_arr.push('<tr>'); for (var j = 0; j < $datagrid.columns.length; j++) { var column = this.columns[j]; html_arr.push('<td>'); if (column.field == "$index") { html_arr.push(this.pageSize * (this.pageIndex - 1) + i + 1); } else { var renderValue = getRenderValue(item, column); html_arr.push(renderValue); } html_arr.push('</td>'); } html_arr.push('</tr>'); } html_arr.push('</tbody>'); var tbody = $datagrid.$table.find('tbody'); if (tbody.length > 0) { tbody.replaceWith(html_arr.join('')); if (this.isSearch) { $datagrid.pager.render(data.PageCount, data.PageIndex); this.isSearch = false; } } else { $datagrid.$table.append(html_arr.join('')); //分页 this.pager = new dw.Pager($datagrid.$table.find('.page'), data.PageCount, data.PageIndex, function (num) { $datagrid.pageIndex = num; $datagrid.load($datagrid.params); }) } }; function urlMapping(id, list) { for (var i = 0; i < list.length; i++) { var item = list[i]; if (id == item.ID) { return item.Name; } } return ''; } function getRenderValue(item, column) { var val = item[column.field]; if (column.formatter) { return eval(column.formatter + '(val,item)'); } else if (column.url) { var list = dw.getCache(column.url); return urlMapping(val, list); } else { if (typeof (val) === "string") { if (/Date\([\d+]+\)/.test(val)) { var date = eval(val.replace(/\/Date\((\d+)\)\//gi, "new Date($1)")); val = dw.dateFormat(date, column.format || 'yyyy-MM-dd hh:mm'); return val; } } return val; } } dw.DataGrid = DataGrid; }('DataGrid', dw)); (function (dwName, dw) { function Select($select, params) { this.name = dwName; var args = $.extend({ value: undefined, onInit: undefined, hasDefault: true, defaultName: '请选择', defaultID: '' }, params); this.$select = $select; var url = $select.data('url'); var list = dw.getCache(url); fillSelect(list); //[{ID:id,Name:name}] function fillSelect(list) { var htmlArr = []; if (args.hasDefault) { htmlArr.push('<option value="' + args.defaultID + '">' + args.defaultName + '</option>'); } for (var i = 0; i < list.length; i++) { var item = list[i]; htmlArr.push('<option value="' + item.ID + '">' + item.Name + '</option>'); } $select.html(htmlArr.join('')); if (args.value !== undefined) { $select.val(args.value); } if (args.onInit && typeof (args.onInit) === "function") { args.onInit(list); } } } dw.Select = Select; }('Select', dw));
data-widget.js