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时写法是不一样的

 最后效果图

posted @ 2012-11-09 10:20  Zux  阅读(601)  评论(0编辑  收藏  举报