YUI3下widget的plugin开发
最近在尝试使用YUI3重建ExtFrame框架,使用YUI3做为更佳的UI和JS支持
和ExtJS比,YUI3的UI看起来缺少了足够的UI控件,但是,YUI3的widget开发更灵活(也更难掌握),YUI3的widget操作更多的基于DOM封装的Yui Node而不是象ExtJS一样基于Component,扩展性更灵活
在尝试将一些逻辑封装到YUI的DataTable里时遇到了点问题,在ExtJS里,可以直接通过Ext.extend建立Ext.Grid的子件,并向子件里添加属性和方法,这样,可以直接调用生成的子件就可以了,但是YUI3的widget在使用Y.Extend后,UI完全没能被正常渲染出来
经过上论坛咨询,老外给了些意见,可以使用Y.Base.create来创建子件,但是同样失败了,并且有人说子件不能正确渲染可能是widget名称相关的BUG,但是这可能需要相关开发人员来检测
然后老外给的意见是使用plugin技术,并给了2个demo网页,仔细看了后,确实是个不错的主意,并且尝试建立自己的plugin后确认,YUI3的plugin确实是个好东西
第一个demo是DataTableFooter,在表格下面增加了一行统计的页脚,https://github.com/stlsmiths/YUI3-datatable-footer,这个demo确实不错,但是有点老,里面的DataTable版本其实已经被大幅度修正过了(生成的表格的HTML代码都和现在版本不一样,应当是过期版本),很多代码已经不能在现在版本下正确运行,照着写了些东西后调试修改了好几天才能正确运行
第二个demo地址是http://blunderalong.com/yui,里面有很多DataTable的扩展,其中的Paginator Model-View就是翻页和自动读取数据的Demo,不过这个demo修改了DataTable内核,一旦引入,页面上所有DataTable都会自动使用,而不能分开
最后还是以第一个demo为基础,编写了2个DataTable的plugin
YUI3的plugin技术,简单说就是为widget提供一个接口,当widget进行renderUI、syncUI等动作或事件时,plugin可以进行相应并动作
第一个plugin是为DataTable增加checkbox功能
标准的DataTable的checkbox功能,是在创建时为column列增加checkbox列配置
http://yuilibrary.com/yui/docs/datatable/datatable-chkboxselect.html
var table = new Y.DataTable({ columns : [ { key: 'select', allowHTML: true, // to avoid HTML escaping label: '<input type="checkbox" class="protocol-select-all" title="Toggle ALL records"/>', formatter: '<input type="checkbox" checked/>', emptyCellValue: '<input type="checkbox"/>' }, { key: 'port', label: 'Port No.' }, { key: 'pname', label: 'Protocol' }, { key: 'ptitle', label: 'Common Name' } ], data : ports, scrollable: 'y', height : '250px', sortable : ['port','pname'], sortBy : 'port', recordType: ['select', 'port', 'pname', 'ptitle'] }).render("#dtable");
之所以编写checkboxplugin,是因为我在视图将columns全部通过配置来动态生成,而checkbox写到配置里是比较麻烦的
修改后的代码是这样的:
var table = new Y.DataTable({ columns: Y.TableConfig.Get('plan'), scrollable: 'y', height: '200px', data: ports }).plug(Y.DataTablePlugin.CheckboxPlugin).render('#dtable');
这样,通过自己编写的TableConfig获取配置的column信息(以后页面上编写脚本时无需再编写繁琐的column参数),再通过checkboxplugin生成checkbox列
这个写法和原本Datatable设计有点不同的地方时,在遇到特殊格式时,columns列是通过formatter参数指定格式或格式化的function,而我的设计里不在前台处理,所以特殊的格式都放到后台生成数据后使用对应的render来处理
checkboxplugin是这样写的
function CheckboxPlugin(config) { CheckboxPlugin.superclass.constructor.apply(this, arguments); }; Y.mix(CheckboxPlugin, { NAME: 'checkboxPlugin', NS: 'checkboxplugin', ATTRS: { place: { value: 0, validator: Y.Lang.IsNumber } } }); Y.extend(CheckboxPlugin, Y.Plugin.Base, { initializer: function() { var dt = this.get('host'); dt.addColumn(checkbox, this.get('place')); dt.delegate("click", function(e) { var checked = e.target.get('checked') || undefined; this.getRecord(e.target).set('select', checked); this.get('contentBox').one('.protocol-select-all').set('checked', false); }, ".yui3-datatable-data .yui3-datatable-col-select input", dt); dt.delegate('click', function(e) { var checked = e.target.get('checked') || undefined; this.data.invoke('set', 'select', checked, { silent: true }); this.syncUI(); }, '.protocol-select-all', dt); } }); Y.namespace('DataTablePlugin'); Y.DataTablePlugin.CheckboxPlugin = CheckboxPlugin;
可以看到,这样写的另一个好处是已经自动将全选事件封装进去了,无需每个datatable创建后再添加一次此事件
从上面代码可以看出,plugin的基础写法很简单,主要是在扩展Y.Plugin.Base时,在initialize事件里写出额外的动作来
在initialize事件里,也可以为host的datatable添加render等事件响应
写法是:this.afterHostMethod('方法名', 方法)或this.afterHostEvent('事件名', 方法)
也可以使用beforeHostMethod或onHostEvent
一般widget就三个主要事件:renderUI,syncUI,bindUI,方法主要是render等
下面是我参考编写的DataTableFooter,用来翻页的,不过这个plugin不能单独工作,需要和DataReaderPlugin协调(还没写好),目前也就完成了UI部分
function DataTablePageBar(config) { DataTablePageBar.superclass.constructor.apply(this, arguments); }; Y.mix(DataTablePageBar, { NAME: 'dataTablePageBar', NS: 'pageBarPlugin', ATTRS: { place: { value: 0, validator: Y.Lang.IsNumber }, fixed: { value: false, validator: YLang.isBoolean }, info: { value: { pagecount: 1, currentpage: 1, totalcount: 1 }, setter: function(val) { this._tfootThNode.one('.page').set('text', '第' + val.currentpage + '页/共' + val.pagecount + '页 ' + val.totalcount + '条记录'); return val; } } } }); Y.extend(DataTablePageBar, Y.Plugin.Base, { _tfootContainer: null, _tfootNode: null, _tfootTrNode: null, _tfootThNode: null, initializer: function() { this.afterHostMethod("render", this.renderPluginUI); }, renderPluginUI: function() { var dt = this.get(HOST), hdg = this.get(FOOT_HDG), tfootParent = dt._parentNode; if (dt.get("contentBox").one(".yui3-datatable-y-scroller-container")) { this._tfootNode = dt.get("contentBox").one(".yui3-datatable-y-scroller-container").appendChild(YCreate("<table width='100%'/>")).appendChild(YCreate("<tfoot/>")); } else { this._tfootNode = dt.get("contentBox").one(".yui3-datatable-table").appendChild(YCreate("<tfoot/>")); } this._tfootTrNode = this._tfootNode.appendChild(YCreate("<tr/>")); this._tfootThNode = this._tfootTrNode.appendChild(YCreate("<th colSpan='" + dt.get('columns').length + "'/>")); this._tfootThNode.addClass('yui3-datatable-ftth'); this._tfootThNode.appendChild(YCreate('<span><a class="firstpage" href="#"><img src="../image/page-first.gif" /></a></span><span><a class="prevpage" href="#"><img src="../image/page-prev.gif" /></a></span><span><a class="nextpage" href="#"><img src="../image/page-next.gif" /></a></span><span><a class="lastpage" href="#"><img src="../image/page-last.gif" /></a></span><span class="page"></span><span class="jump">跳转到第<input type="text" style="width:20px" maxlength="3">页<a class="jumppage" href="#"><image src="../image/go.gif" /></a></span>')); } });
说明:YCreate就是Y.Node.Create,另外还有行代码 Y.DataTablePlugin.PageBarPlugin = DataTablePageBar;在最后
中间创建tfoot的代码,原来那个demo已经太老完全不对,通过检查生成的html后修改成为现在的样子,有无scroll时写法是不一样的
最后效果图