一,Jquery Widget Factory介绍

官网地址

Demo:http://jqueryui.com/widget/

API:http://api.jqueryui.com/jQuery.widget/

常见的用jquery写UI插件有两种方式:1)对JQuery自身的扩展插件,形如$.extend(方法名字:function(参数){方法体})。2)对Jquery对象的拓展,形如(function ($) {$.fn.方法名 = function(参数){方法体}})(jQuery);以上两种方式编写的插件大都是无状态的,即和外部的交互时机仅限于调用它的时候,如$(ele).val(value),但是一些复杂的UI插件却要求是有状态的,有生命周期的,从在DOM中被创建到销毁的整个过程中都要和外部有交互,Jquery Widget factory 是创建有状态的插件的唯一途径。

二,我用Widget写表格的需求介绍

在公司的BSS系统中用户在填写表单一些明细的数据的时候要求

1)单元格可以编辑

2)可以插入新行

3)可以删除任意行

4)可以多选

5)可以实时统计数值型列的合计

6)支持和页面其它的DOM元素的联动

7)可以导入Excel

8)整个表格的操作过程页面不能刷新(最重要的需求)

大致形状如此:

三,我用Jquery Widget的实现

$.widget("ui.AFGrid", {
options: {
Title: "",//表格头
HeadRow: "",//表格头html模板
InsertRow: "",//插入行html模板
TotalRow: "",//合计行html模板
DataSource: "",//数据源
ArrayDelimiter: ";",//数据行分隔符
FieldDelimiter: "#",//数据列分隔符
IsReadOnly: false,//是否只读
IsAdd: false,//在为插件对象重新指定数据源时该选项用于指示是否清空表格中原有的数据,true不清空,false清空
/*call backs*/
Change: null, //编辑表格后blur事件触发的方法,参数(jquery对象){calculateRow:合计行Tr,contentBody明细表格Tbody,currentRow:当前行Tr}
Validate: null, //编辑表格后blur事件触发的方法(此方法首先触发,返回true时,随后触发Change方法),参数(jquery对象){currentRow:当前行Tr}
InsertRowCalculate: null, //点击插入新行click事件触发的方法,参数(jquery对象){insertRow:插入行Tr}
TableCalculate: null//明细表格计算,参数(jquery对象){calculateRow:合计行Tr,contentBody明细表格Tbody}
},
_create: function () {
$this = this;
this.trTitleEle = $("<tr id='tr_Title' align='center' class='theme'></tr>").appendTo(this.element);
this.trSelectEle = $("<tr id='tr_Select'></tr>").appendTo(this.element);
this.trContentEle = $("<tr id='tr_Content'></tr>").appendTo(this.element);
this.trEditEle = $("<tr id='tr_Edit'></tr>").appendTo(this.element);
//trTitle
this.trTitleTdEle = $("<td colspan='9999'>" + this.options.Title + "</td>").appendTo(this.trTitleEle);
//trSelect
this.trSelectTdEle = $("<td colspan='9999'></td>").appendTo(this.trSelectEle);
if (this.options.IsReadOnly) {
this.trSelectTdEle.hide();
}
if (!this.options.IsReadOnly) {
this.trSelectA_AllEle = $("<a href='#'>全选</a>").appendTo(this.trSelectTdEle);
this.trSelectA_ExceptEle = $("<a href='#'>反选</a>").appendTo(this.trSelectTdEle);
this.trSelectA_DelEle = $("<a href='#'>删除</a>").appendTo(this.trSelectTdEle);
//全选
this._on(this.trSelectA_AllEle, { "click": function (event) { this.trContentTdTableBodyEle.find("input[type='checkbox']").prop("checked", true); } });
//反选
this._on(this.trSelectA_ExceptEle, { "click": function (event) {
this.trContentTdTableBodyEle.find("input[type='checkbox']").each(function (i, n) {
if ($(n).prop("checked")) { $(n).prop("checked", false); } else { $(n).prop("checked", true); }
});
}
});
//删除
this._on(this.trSelectA_DelEle, { "click": function (event) {
this.trContentTdTableBodyEle.find("input[type = 'checkbox']").each(function (i, n) {
if ($(n).prop("checked")) {
$(n).parent().parent().remove();
}
});
this._trigger("TableCalculate", null, { calculateRow: this.TotalRowEle, contentBody: this.trContentTdTableBodyEle });
}
});
}
//trEdit
this.trEditTdEle = $("<td></td>").appendTo(this.trEditEle);
this.trEditTdTableEle = $("<table width='100%' border='1'></table>").appendTo(this.trEditTdEle);
this.TotalRowEle = $(this.options.TotalRow).appendTo(this.trEditTdTableEle);
this.InsertRowEle = $(this.options.InsertRow).appendTo(this.trEditTdTableEle);
var insertTdTotal = this.InsertRowEle.find("td").length - 1;
this.importRowEle = $("<tr></tr>").appendTo(this.trEditTdTableEle);
$("<td><a href='#'>导入</a></td><td colspan='" + insertTdTotal + "'><input type='file'/><a href='#'>下载模版</a></td>").appendTo(this.importRowEle);
this.InsertRowEle.find("input[type='text'][class='thousandSeparator']").on("blur", function (event) {
if ($(event.target).val() !== "") {
addThousandSeparator($(event.target));
if ($this._trigger("Validate", event, { currentRow: $this.InsertRowEle }) !== false) {
$this._trigger("InsertRowCalculate", event, { insertRow: $this.InsertRowEle });
}
$(event.target).css("borderColor", "");
}
else {
$(event.target).css("borderColor", "Red");
event.preventDefault();
}
});
if (this.options.IsReadOnly) {
this.InsertRowEle.hide();
}
if (!this.options.IsReadOnly) {
//插入
this._on(this.InsertRowEle.find("a").first(), { "click": function (event) {
if (this._trigger("Validate", event, { currentRow: this.InsertRowEle }) !== false) {
if (this._trigger("InsertRowCalculate", event, { insertRow: this.InsertRowEle }) !== false) {
var $tempTr = $(this.options.InsertRow);
$tempTr.find("input[type='text']").on("blur", function (event) {
if ($(event.target).val() !== "") {
if ($(event.target).attr("class") === "thousandSeparator") {
addThousandSeparator($(event.target));
}
$this._trigger("Change", event, { calculateRow: $this.TotalRowEle, contentBody: $this.trContentTdTableBodyEle, currentRow: $(event.target).parent().parent() });
$(event.target).css("borderColor", "");
}
else {
$(event.target).css("borderColor", "Red");
event.preventDefault();
}
});
$tempTr.find("select").on("change", function (event) {
if ($(event.target).val() !== "0") {
$this._trigger("Change", event, { calculateRow: $this.TotalRowEle, contentBody: $this.trContentTdTableBodyEle, currentRow: $(event.target).parent().parent() });
$(event.target).css("borderColor", "");
}
else {
$(event.target).css("borderColor", "Red");
event.preventDefault();
}
}); $tempTr.find("td:first-child").html("<input type='checkbox'/>");
this.InsertRowEle.find("td").each(function (index, ele) {
if (index > 0) {
$tempTr.find("td:eq(" + index + ")").children().first().val($(ele).children().first().val());
}
});
$tempTr.appendTo(this.trContentTdTableBodyEle);
this._trigger("TableCalculate", null, { calculateRow: this.TotalRowEle, contentBody: this.trContentTdTableBodyEle });
}
}
}
});
}
//trContent
this.trContentTdEle = $("<td></td>").appendTo(this.trContentEle);
this.trContentTdTableEle = $("<table width='100%' border='1'></table>").appendTo(this.trContentTdEle);
this.trContentTdTableHeadEle = $("<thead></thead>").appendTo(this.trContentTdTableEle);
this.trContentTdTableBodyEle = $("<tbody></tbody>").appendTo(this.trContentTdTableEle);
this.trContentTdTableHeadRowEle = $(this.options.HeadRow).appendTo(this.trContentTdTableHeadEle);
this.setContent();
},
_refresh: function () {
if (this.options.IsReadOnly) {
this.trSelectTdEle.hide();
this.InsertRowEle.hide();
}
else {
this.trSelectTdEle.show();
this.InsertRowEle.show();
}
this.setContent();
},
_setOptions: function () {
this._superApply(arguments);
this._refresh();
},
_setOption: function (key, value) {
this._super(key, value);
},
setContent: function () {
$this = this;
if (this.options.DataSource.length > 0) {
if (!this.options.IsAdd) {
this.trContentTdTableBodyEle.empty();
}
var arrTr = this.options.DataSource.split(this.options.ArrayDelimiter);
for (var tr in arrTr) {
if (arrTr[tr].length > 0) {
var $tempTr = $(this.options.InsertRow);
$tempTr.find("input[type='text']").on("blur", function (event) {
if ($(event.target).val() !== "") {
if ($(event.target).attr("class") === "thousandSeparator") {
addThousandSeparator($(event.target));
}
$this._trigger("Change", event, { calculateRow: $this.TotalRowEle, contentBody: $this.trContentTdTableBodyEle, currentRow: $(event.target).parent().parent() });
$(event.target).css("borderColor", "");
}
else {
$(event.target).css("borderColor", "Red");
event.preventDefault();
}
}); $tempTr.find("select").on("change", function (event) {
if ($(event.target).val() !== "0") {
$this._trigger("Change", event, { calculateRow: $this.TotalRowEle, contentBody: $this.trContentTdTableBodyEle, currentRow: $(event.target).parent().parent() });
$(event.target).css("borderColor", "");
}
else {
$(event.target).css("borderColor", "Red");
event.preventDefault();
}
});
$tempTr.find("td:first-child").html("<input type='checkbox'/>");
if (this.options.IsReadOnly) {
$tempTr.find("td:first-child").html("");
}
var con = arrTr[tr].split(this.options.FieldDelimiter);
for (var i = 1; i <= con.length; i++) {
var $element = $tempTr.find("td:eq(" + i + ")").children().first();
$element.val(con[i - 1]);
if ($element.attr("class") === "thousandSeparator") {
$element.val(getValueWithThousandSeparator(con[i - 1]));
}
if (this.options.IsReadOnly) {
$element.attr("disabled", "disabled");
if ($element.attr("class") === "Wdate") {
$element.unbind();
}
}
}
$tempTr.appendTo(this.trContentTdTableBodyEle);
}
}
this._trigger("TableCalculate", null, { calculateRow: this.TotalRowEle, contentBody: this.trContentTdTableBodyEle });
}
}
});

四,调用方式

首先,在页面中指定一个table标签,<table id="test_tb" width="100%"></table>,然后

 var test = $("#test_tb").AFGrid({
IsReadOnly: false,
TotalRow: "<tr><td width='10%'></td><td width='22.5%'></td><td width='22.5%' align='right'>合计金额</td><td align='left'><label id='lbl_RecordsTotal'></label></td><td width='22.5%'></td></tr>",
InsertRow: "<tr><td width='10%'><a href='#' >插入项</a></td><td width='22.5%'><input type='text' style='width: 100%' /></td><td width='22.5%'><input type='text' class='Wdate' onclick='WdatePicker()' style='width: 100%' /></td><td width='22.5%'><input type='text' style='width: 100%' onkeydown='onlydigital()' class='thousandSeparator'/></td><td width='22.5%'><select style='width: 100%'><option value='1'>正常</option><option value='2'>退票</option></select></td></tr>",
DataSource: $("#<%=hf_test.ClientID %>").val(),
HeadRow: "<tr><th width='10%'></th><th width='22.5%'>发票编号</th><th width='22.5%'>开具时间</th><th width='22.5%'>发票金额</th><th width='22.5%'>状态</th><tr>",
Title: "执行记录",
Change: function (event, data) {
if (ValidateTb(event, data)) {
CalculateTb(event, data);
}
},
Validate: function (event, data) {
ValidateTb(event, data);
},
InsertRowCalculate: function (event, data) { },
TableCalculate: function (event, data) {
CalculateTb(event, data);
}
});

效果如图:

随后可以通过改变IsReadOnly选项控制表格的可编辑性,可以重新指定数据源DataSource并结合IsAdd选项的设置为通过ajax获取的数据动态的添加或者刷新到表格上,代码详见http://files.cnblogs.com/afutureBoss/AFWidget.rar

用Jquery Widgets Factory写自己的表格控件——AFGrid(支持增、删、改)的更多相关文章

  1. 一个动态扩展表格控件列和行的 jQuery 插件

    一个动态扩展表格控件列和行的 jQuery 插件 不过这并不影响使用鸭! 看这里:https://github.com/zhuwansu/table-ext.js 一个简单的示范 html <t ...

  2. tbl.js div实现的表格控件,完全免费,no jquery

    html上现在有比较好用的表格控件是datatable,但是编辑.按钮等部分是收费的,只有基础功能免费.而且尺寸发生变化时需要手工刷新等繁琐操作较多.所以我开发一个免费的供大家使用. 本项目已用于“虚 ...

  3. tbl.js div实现的表格控件,完全免费,不依赖jquery

    html上现在有比较好用的表格控件是datatable,但是编辑.按钮等部分是收费的,只有基础功能免费.而且尺寸发生变化时需要手工刷新等繁琐操作较多.所以我开发一个免费的供大家使用. 本项目已用于&q ...

  4. 分享12款 JavaScript 表格控件(DataGrid)

    JavaScript 表格控件可以操作大数据集的 HTML 表格,提供各种功能,如分页.排序.过滤以及行编辑.在本文中,我们整理了13个最好的 JavaScript 表格插件分享给开发人员,开发者可以 ...

  5. 葡萄城首席架构师:前端开发与Web表格控件技术解读

    讲师:Issam Elbaytam,葡萄城集团全球首席架构师(Chief Software Architect of GrapeCity Global).曾任 Data Dynamics.Inc 创始 ...

  6. 如何在web中实现类似excel的表格控件

    Execl功能非常强大,内置的很多函数或公式可以大大提高对数据的加工处理能力.那么在web中有没有类似的控件呢?经过一番搜寻,发现handsontable具备了基本的excel功能支持公式,同时能对数 ...

  7. Android入门之GridView(表格控件)

    GridView是一个表格控件,可以在每个单元格中显示自定义的View或者字符串.在这里我们要实现一个图标下方有文字的效果. 1.首先我们应自定义布局文件image_text.xml.代码如下: &l ...

  8. ExtJS4.2学习(10)分组表格控件--GroupingGrid(转)

    鸣谢网址:http://www.shuyangyang.com.cn/jishuliangongfang/qianduanjishu/2013-11-17/179.html ------------- ...

  9. Ext入门学习系列(五)表格控件(3)

    上节学习了Ext中如何绑定服务器端传递的数据.分别用asp.net和asp.net MVC.PHP.XML为例.本节主要介绍绑定之后的分页功能. 一.Ext的表格控件如何绑定? 分页是Ext自带的一个 ...

随机推荐

  1. android真机自动化测试

    appium执行用例时报错问题: 问题解析: 一般该种情况都是因为来连接了多个设备,验证办法:cmd->执行adb devices  看结果是否是多个devices ,如果是这个问题,停掉多余设 ...

  2. 数论之高次同余方程(Baby Step Giant Step + 拓展BSGS)

    什么叫高次同余方程?说白了就是解决这样一个问题: A^x=B(mod C),求最小的x值. baby step giant step算法 题目条件:C是素数(事实上,A与C互质就可以.为什么?在BSG ...

  3. gcc: error trying to exec 'cc1plus': execvp: 没有那个文件或目录

    问题: 解决办法: 1.没安装G++ 可使用 sudo apt-get install g++ 进行安装 2.gcc版本和g++版本不相符 可利用gcc -v和g++ -v 查看版本

  4. TableViewCell自定义分割线

    产品设计的要求cell的分割线长度不用是整个屏幕宽,并且设计要求分割线为2px(两条),上下不同色. 实现如下: UITableView中将分割线样式改为None tableView.separato ...

  5. Mysql笔记——触发器简单实例

    首先贴上触发器语法吧: CREATE TRIGGER <触发器名称> –触发器必须有名字,最多64个字符,可能后面会附有分隔符.它和MySQL中其他对象的命名方式基本相象. { BEFOR ...

  6. PV UV

    定义 PV: Page View       页面浏览量或点击量,用户每次刷新即被计算一次. UV: Unique Visitor  就是有多少个IP数量.就是指的有多少人在访问你的店.每个人用的电脑 ...

  7. java读取某个文件夹下的所有文件

    import java.io.FileNotFoundException;import java.io.IOException;import java.io.File; public class Re ...

  8. git 使用(二)

    之前写过一篇git使用(一),那是入门篇,现在的(二)可以说是进阶篇吧,主要讲一些使用过程的注意事件及相关问题的解决办法. 一.push和fetch还需要输入用户名和密码? 解决办法:看看公玥是否添加 ...

  9. 【USACO】【Section1.1】Greedy Gift Givers

    小白题,也没啥好说的.关键我的算法感觉特别菜的一点是每次要遍历数组从人名找对应的编号,这个效率就很低了.看了ANALYZE里面也是这样的.不过它比我好的一点是我多余设置了initial_money变量 ...

  10. 别在细节上栽跟头------------mysql 字段类型详解

    也许你平时不在意,在设计数据库的时候,数字就设成int(10) 字符串就设成varchar(20)或者text 普通情况下是没有问题的,但是若不理解字段类型和长度的含义,总有一天你会在这里栽跟头, 这 ...