当前位置: 代码迷 >> Web前端 >> 如何样用扩展的方式来扩展OperaMasksUI的grid
  详细解决方案

如何样用扩展的方式来扩展OperaMasksUI的grid

热度:337   发布时间:2012-08-27 21:21:57.0
怎么样用扩展的方式来扩展OperaMasksUI的grid

前面我写了一篇文章来扩展OpreaMasksUI的grid的排序功能和显示详情的功能,不清楚的可以看下我博客的另外一篇文章:http://dingchao-lonton.iteye.com/admin/blogs/1345088,但是我修改的时候污染了原来的代码,我觉得这种方式很不好,所以我就用jquery ui的widget factory提供的继承方式来扩展,这样原来的代码可以保持干净;

?

javasscript代码如下:

?

?

/*
 * $Id: om-grid.js,v 1.97 2012/01/04 03:28:04 zhoufazhi Exp $
 * operamasks-ui omGrid @VERSION
 *
 * Copyright 2011, AUTHORS.txt (http://ui.operamasks.org/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://ui.operamasks.org/license
 *
 * http://ui.operamasks.org/docs/
 * 
 * Depends:
 *  jquery.ui.widget.js
 *  jquery.ui.core.js
 *  jquery.ui.mouse.js
 *  jquery.ui.resizable.js
 *  om-grid.js
 */ 
/**
     * @name omGridExtend
     * @author ding chao
     * @class 表格组件。扩展自om.omGrid,增加了排序和显示详情功能<br/><br/>
     * <b>示例:</b><br/>
     * <pre>
     * &lt;script type="text/javascript" >
     *   $('#grid').omGridExtend({
	 *			//是否显示详情
	 *			showDetail:true,
	 *			singleSelect:false,
     *          dataSource : 'griddata.do?method=fast',
     *           colModel : [ {header : 'ID', name : 'id', width : 100, align : 'center'}, 
     *                        {header : '地区', name : 'city', width : 120, align : 'center'}, 
     *                        {header : '地址', name : 'address', align : 'center', width : 'autoExpand'} ],
     *				onRowClick:function(index,row){
	 *				
	 *			},
	 *			onRowSelect:function(){
	 *				
	 *			},
	 *			onRowDeselect:function(){
	 *				
	 *			},
	 *			//当详情展开的时候调用的回调
	 *			onDetailExpand:function(rowData,row){
	 *				
	 *			},
	 *			//初始化的时候以哪一个字段排序
	 *			sortName:'address',
	 *			//排序方式
	 *			sortOrder:'desc',
	 *			method:'get',
	 *			onRowCheck:function(index){
	 *				
	 *			}
     *      });
     * &lt;/script>
     * 
     * &lt;table id="mytable"/>
     * </pre>
     * 
     */
;(function($) {
    $.widget('om.omGridExtend',$.om.omGrid, {
        options:/** @lends omGridExtend#*/{
			/**
			  *是否显示详情
			  *@default false
			  *@type Boolean
			  *@example
			  *$('.selector').omGrid({showDetail : false});
			  */
			showDetail:false,
			/**
			  *详情展开的时候是否重新加载
			  *@default true
			  *@type Boolean
			  *@example
			  *$('.selector').omGrid({detailExpandReload : false});
			  */
			detailExpandReload:true,
            /**
			  *详情展开时候的回调函数
			  *@default function(rowData,detailContainer){}
			  *@type Function
			  *@example
			  *$('.selector').omGrid({onDetailExpand : function(rowData,detailContainer){}});
			  */
			onDetailExpand : function(rowData,detailContainer){},
			/**
			  *初始化的时候以哪个字段作为排序字段
			  *@default null
			  *@type String
			  *@example
			  *$('.selector').omGrid({sortName : 'address'});
			  */
			sortName:null,
			/**
			  *排序方式  enmu{"desc","asc"}
			  *@default asc
			  *@type String
			  *@example
			  *$('.selector').omGrid({sortName : 'address'});
			  */
			sortOrder:'asc'
        },
        //private methods
        _create:function(){
			//call super method
			$.om.omGrid.prototype._create.call(this);

            this._bindHeadClickEnvent();
			this._bindDetailEvent();
        },
        _buildTableHead:function(){
            var op=this.options,
                el=this.element,
                grid = el.closest('.om-grid'),
                cms=op.colModel,
                allColsWidth = 0, //colModel的宽度
                indexWidth = 0, //colModel的宽度
                checkboxWidth = 0, //colModel的宽度
                autoExpandColIndex = -1;
                thead=$('<thead></thead');
                tr=$('<tr></tr>').appendTo(thead);
				this.detailColSpan = 0;
			//渲染显示明细列
			if(op.showDetail){
				var cell = $('<th></th>').attr({axis:'detailCol',align:'center'}).addClass('unsort detailCol').append($('<div class="indexheader" style="text-align:center;width:25px;"></div'));
				tr.append(cell);
                indexWidth=25;
				this.detailColSpan++;
			}
            //渲染序号列
            if(op.showIndex){
                var cell=$('<th></th').attr({axis:'indexCol',align:'center'}).addClass('unsort indexCol').append($('<div class="indexheader" style="text-align:center;width:25px;"></div'));
                tr.append(cell);
                indexWidth=25;
				this.detailColSpan++;
            }
            //渲染checkbox列
            if(!op.singleSelect){
                var cell=$('<th></th').attr({axis:'checkboxCol',align:'center'}).addClass('unsort checkboxCol').append($('<div class="checkboxheader" style="text-align:center;width:17px;"><span class="checkbox"/></div'));
                tr.append(cell);
                checkboxWidth=17;
				this.detailColSpan++;
            }
            //渲染colModel各列
            for (var i=0,len=cms.length;i<len;i++) {
                var cm=cms[i],cmWidth = cm.width || 60,cmAlign=cm.align || 'center';
                if(cmWidth == 'autoExpand'){
                    cmWidth = 0;
                    autoExpandColIndex = i;
                }
                var thCell=$('<div></div').html(cm.header).css({'text-align':cmAlign,width:cmWidth});
				if(this.options.sortName && this.options.sortName === cm.name){
					if(this.options.sortOrder === 'asc'){
						thCell.addClass('sasc');
					}else{
						thCell.addClass('sdesc');
					}
				}
                cm.wrap && thCell.addClass('wrap');
                var th=$('<th></th').attr('axis', 'col' + i).addClass('col' + i).append(thCell);
                if(cm.name) {
                    th.attr('abbr', cm.name);
                }
                if(cm.align) {
                    th.attr('align',cm.align);
                }
                //var _div=$('<div></div>').html(cm.header).attr('width', cmWidth);
                allColsWidth += cmWidth;
                tr.append(th);
				this.detailColSpan++;
            }
            //tr.append($('<th></th'));
            el.prepend(thead);
            
            var hDiv = $('<div class="hDiv om-state-default"></div>').append('<div class="hDivBox"><table cellPadding="0" cellSpacing="0"></table></div>');
            el.parent().before(hDiv);
            
                $('table',hDiv).append(thead);
            //修正各列的列宽
            if(autoExpandColIndex != -1){ //说明有某列要自动扩充
                var tableWidth=grid.width()-20,
                    //usableWidth=tableWidth-allColsWidth-indexWidth-checkboxWidth;
                    usableWidth=tableWidth-thead.width();
                    toBeExpandedTh=tr.find('th[axis="col'+autoExpandColIndex+'"] div');
                if(usableWidth<=0){
                    toBeExpandedTh.css('width',60);
                }else{
                    toBeExpandedTh.css('width',usableWidth);
                }
            }else if(op.autoFit){
                //var tableWidth=el.width(),
                 //   usableWidth=tableWidth-indexWidth-checkboxWidth;
                var tableWidth=grid.width()-20,
                    usableWidth=tableWidth-thead.width(),
                    percent=1+usableWidth/allColsWidth,
                    toFixedThs=tr.find('th[axis^="col"] div');
                for (var i=0,len=cms.length;i<len;i++) {
                    var col=toFixedThs.eq(i);
                    col.css('width',parseInt(col.width()*percent));
                }
            }
            this.thead=thead;
            thead = null;
        },
		_bindHeadClickEnvent:function(){
			var self = this,
				op = this.options;
			this.thead.delegate('th:not(.unsort)','click',function(event){
				var abbr = $(this).attr('abbr');
				op.sortName = abbr;
				$('th>div',self.thead).removeClass('sasc').removeClass('sdesc');
				op.sortOrder === 'asc' ? (op.sortOrder = 'desc',$('>div',this).addClass('sdesc')):(op.sortOrder = 'asc',$('>div',this).addClass('sasc'));
				self._populate();
			});
			 
		},
		_renderDatas:function(from,to){
            var self=this,
                el=this.element,
                op=this.options,
                grid=el.closest('.om-grid'),
                gridHeaderCols=$('.hDiv thead tr:first th',grid),
                rows=this.pageData.data.rows,
                colModel=op.colModel,
                rowClasses=op.rowClasses,
                tbody=$('tbody',el).empty(),
                isRowClassesFn= (typeof rowClasses === 'function'),
                pageData = this.pageData,start=(pageData.nowPage-1)*op.limit;
            $.each(rows,function(i, rowData) {
                var rowCls = isRowClassesFn? rowClasses(i,rowData):rowClasses[i % rowClasses.length];
                var tr=$('<tr></tr>').addClass('om-grid-row').addClass(rowCls).attr('rowindex',i);
                var rowValues=self._buildRowCellValues(colModel,rowData,i);
                $(gridHeaderCols).each(function(){
                    var axis = $(this).attr('axis'),wrap=false,html;
					if(axis == 'detailCol'){
						 html = '<span class="detailIcon collapsed"/>';
					}
                    else if(axis == 'indexCol'){
                        html=i+start+1;
                    }else if(axis == 'checkboxCol'){
                        html = '<span class="checkbox"/>';
                    }else{
                        var colIndex=axis.substring(3);
                        html=rowValues[colIndex];
                        if(colModel[colIndex].wrap){
							wrap=true;
						} 
                    }
                    var td = $('<td></td>').attr({align:this.align,abbr:this.abbr}).addClass(axis).append($('<div></div>').html(html).addClass(wrap?'wrap':'').attr({'align':this.align}).width($('div',$(this)).width()));
                    tr.append(td);
                });
                tbody.append(tr);
            });
        },
        //刷新数据
        _populate : function() { // get latest data
            var self=this,
                el = this.element,
                grid = el.closest('.om-grid'),
                op = this.options,
                pageStat = $('.pPageStat', grid);
            if (!op.dataSource) {
                $('.pPageStat', grid).html(op.emptygMsg);
                return false;
            }
            if (this.loading) {
                return true;
            }
            var pageData = this.pageData,
                nowPage = pageData.nowPage || 1,
                loadMask = $('.gBlock',grid);
            //具备加载数据的前提条件了,准备加载
            this.loading = true;
            pageStat.html(op.loadingMsg);
            loadMask.show();
            if ($.browser.opera) {
                $(grid).css('visibility', 'hidden');
            }
            var limit = (op.limit<=0)?100000000:op.limit;
            var param = [ {
                name : 'start',
                value : limit * (nowPage - 1)
            }, {
                name : 'limit',
                value : limit
            }, {
                name : '_time_stamp_',
                value : new Date().getTime()
            } ];
			//push sort options
			if(op.sortName){
				param.push({
					name : 'sortName',
					value : op.sortName 
				});
				param.push({
					name : 'sortOrder',
					value : op.sortOrder
				});
			}
            $.ajax({
                type : op.method,
                url : op.dataSource,
                data : param,
                dataType : 'json',
                success : function(data,textStatus,request) {
                    var onSuccess = op.onSuccess;
                    if (typeof(onSuccess) == 'function') {
                        onSuccess(data,textStatus,request);
                    }
                    self._addData(data);
                    op.onRefresh(nowPage,data.rows);
                    loadMask.hide();
                    self.loading = false;
                },
                error : function(XMLHttpRequest, textStatus, errorThrown) {
                    pageStat.html(op.errorMsg).css('color','red');
                    try {
                        var onError = op.onError;
                        if (typeof(onError) == 'function') {
                            onError(XMLHttpRequest, textStatus, errorThrown);
                        }
                    } catch (e) {
                        // do nothing 
                    } finally {
                        loadMask.hide();
                        self.loading = false;
                        return false;
                    }
                    
                }
            });
        },
        //绑定行选择/行反选/行单击/行双击等事件监听
        _bindSelectAndClickEnvent:function(){
            var self=this;
            //如果有checkbo列则绑定事件
            if(!this.options.singleSelect){ //可以多选
                // 全选/反选,不需要刷新headerChekcbox的选择状态
                $('th.checkboxCol span.checkbox',this.thead).click(function(){
                    var thCheckbox=$(this),trSize=$('tr.om-grid-row',this.tbody).size();
                    if(thCheckbox.hasClass('selected')){ //说明是要全部取消选择
                        thCheckbox.removeClass('selected');
                        for(var i=0;i<trSize;i++){
                            self._rowDeCheck(i);
                        }
                    }else{ //说明是要全选
                        thCheckbox.addClass('selected');
                        for(var i=0;i<trSize;i++){
                            self._rowCheck(i);
                        }
                    }
                });
                //行单击,需要刷新headerChekcbox的选择状态
                this.tbody.delegate('tr.om-grid-row  span.checkbox','click',function(){
                    var row=$(this),index=row.closest('tr',self.tbody).index();
                    if(row.hasClass('selected')){ //已选择
                        self._rowDeCheck(index);
                    }else{
                        self._rowCheck(index);
                    }
                    self._refreshHeaderCheckBox();
                    //self.options.onRowClick(index,self._getRowData(index));
                });
                //行双击
                /*this.tbody.delegate('tr.om-grid-row','dblclick',function(){
                    var row=$(this),index=row.index();
                    if(row.hasClass('om-state-highlight')){ //已选择
                        //do nothing
                    }else{
                        self._rowSelect(index);
                        self._refreshHeaderCheckBox();
                    }
                    self.options.onRowDblClick(index,self._getRowData(index));
                });*/
            }//else{ //不可多选
                //行单击
                this.tbody.delegate('tr.om-grid-row','click',function(event){
					if($(event.target).hasClass('checkbox')||$(event.target).hasClass('detailIcon'))
						return false;
                    var row=$(this),index=row.index();
                    if(row.hasClass('om-state-highlight')){ //已选择
                        // no need to deselect another row and select this row
                    }else{
                        var lastSelectedIndex = $('tr.om-state-highlight',self.tbody).index();
                        (lastSelectedIndex != -1) && self._rowDeSelect(lastSelectedIndex);
                        self._rowSelect(index);
                    }
                    self.options.onRowClick(index,self._getRowData(index));
                });
                
                //行双击,因为双击一定会先触发单击,所以对于单选表格双击时这一行一定是选中的,所以不需要强制双击前选中
                this.tbody.delegate('tr.om-grid-row','dblclick',function(){
                    var index=$(this).index();
                    self.options.onRowDblClick(index,self._getRowData(index));
                });
          //  }
        },
		_bindDetailEvent:function(){
			var self = this;
			this.tbody.delegate('span.detailIcon','click',function(){
				if($(this).hasClass('collapsed')){
					$(this).removeClass('collapsed');
					$(this).addClass('expanded');
					var index = parseInt($(this).parents('tr.om-grid-row').attr('rowindex'));
					self._expand($(this).parents('tr.om-grid-row'),index);
				}else{
					$(this).removeClass('expanded');
					$(this).addClass('collapsed');
					var index = parseInt($(this).parents('tr.om-grid-row').attr('rowindex'));
					self._collapse($(this).parents('tr.om-grid-row'),index);
				}
			});
		},
		//展开
		_expand:function(row,index){
			var rowData = this._getRowData(index);
			var detail = $(row).data('detail');
			if(this.options.detailExpandReload){
				detail = null;
			}
			if(!detail){
				detail = $('<tr class="om-grid-row-detail"><td colspan="'+this.detailColSpan+'"><div class="om-grid-row-detail-wrapper">detail</div></td></tr>').insertAfter($(row)).attr({rowindex:index});
				$(row).data('detail',detail);
				this.options.onDetailExpand(rowData,$('div.om-grid-row-detail-wrapper',detail));
			}else{
				detail.show();
			}
			
		},
		//关闭
		_collapse:function(row,index){
			var rowData = this._getRowData(index);
			var detail = $(row).data('detail');
			if(!detail)
				return;
			if(this.options.detailExpandReload){
				detail.remove();
			}else{
				detail.hide();
			}
		},
        _rowSelect:function(index){
             var el=this.element,
                op=this.options,
                tbody=$('tbody',el),
                tr=$('tr:eq('+index+')',tbody),
                chk=$('td.checkboxCol span.checkbox',tr);
            tr.addClass('om-state-highlight');
            //chk.addClass('selected');
            op.onRowSelect(index,this._getRowData(index));
        },
        _rowDeSelect:function(index){
            var self=this,
                el=this.element,
                op=this.options,
                tbody=$('tbody',el),
                tr=$('tr:eq('+index+')',tbody),
                chk=$('td.checkboxCol span.checkbox',tr);
            tr.removeClass('om-state-highlight');
            //chk.removeClass('selected');
            op.onRowDeselect(index,this._getRowData(index));
        },
		_rowCheck:function(index){
             var el=this.element,
                op=this.options,
                tbody=$('tbody',el),
                tr=$('tr:eq('+index+')',tbody),
                chk=$('td.checkboxCol span.checkbox',tr);
            chk.addClass('selected');
			op.onRowCheck(index,this._getRowData(index));
        },
		_rowDeCheck:function(index){
            var self=this,
                el=this.element,
                op=this.options,
                tbody=$('tbody',el),
                tr=$('tr:eq('+index+')',tbody),
                chk=$('td.checkboxCol span.checkbox',tr);
            chk.removeClass('selected');
        },
        //让列可以改变宽度(index列和checkbox列不可以改变宽度)
        /**
         * 选择行。<b>注意:传入的参数是序号(第一行是0第二行是1)数组(比如[0,1]表示选择第一行和第二行);要想清除所有选择,请使用空数组[]作为参数;只能传入序号数组,如果要做复杂的选择算法,请先在其它地方算好序号数组后后调用此方法;此方法会清除其它选择状态,如选择第1,2行然后setSelections([3])最后结果中只有第3行,如setSelections([3,4]);setSelections([5,6])后只会选择5,6两行</b>。
         * @name omGrid#setSelection
         * @function
         * @param indexes 序号(第一行是0第二行是1)数组。
         * @returns jQuery对象
         * @example
         * //选择表格中第二行、第三行、第五行
         * $('.selector').omGridExtend('setSelection',2);
         * 
         */
        setSelection : function(index){
            var self=this;
            /*if(!$.isArray(indexes)){
                indexes=[indexes];
            }*/
            var selected=this.getSelection();
           /* $(selected).each(function(){
                self._rowDeSelect(this);
            });
            $(indexes).each(function(){
                self._rowSelect(this);
            });*/
			if( selected )
				this._rowDeSelect(selected);
			this._rowSelect( index );
        },
        /**
         * 获取选择的行的行号或行记录。<b>注意:默认返回的是行序号组成的数组(如选择了第2行和第5行则返回[1,4]),如果要返回行记录JSON组成的数组需要传入一个true作为参数</b>。
         * @name omGrid#getSelection
         * @function
         * @param needRecords 参数为true时返回的不是行序号数组而是行记录数组。参数为空或不是true时返回行序号数组。
         * @returns jQuery对象
         * @example
         * var selectedIndexed = $('.selector').omGridExtend('getSelection');
         * var selectedRecords = $('.selector').omGridExtend('getSelection',true);
         * 
         */
        getSelection:function(needRecord){
            //needRecords=true时返回Record[],不设或为false时返回index[]
            var self=this,tr=$('tr.om-state-highlight',this.tbody),result;
            if(needRecord){
                var rows=self.pageData.data.rows;
                if(tr.length == 0){
					result = null;
				}else{
					result = rows[tr.attr('rowindex')];
				}
            }else{
                if(tr.length == 0){
					result = null;
				}else{
					result = tr.attr('rowindex');
				}
            }
            return result;
        },
		 /**
         * 获取选择的行的行号或行记录。<b>注意:默认返回的是行序号组成的数组(如选择了第2行和第5行则返回[1,4]),如果要返回行记录JSON组成的数组需要传入一个true作为参数</b>。
         * @name omGrid#getChecks
         * @function
         * @param needRecords 参数为true时返回的不是行序号数组而是行记录数组。参数为空或不是true时返回行序号数组。
         * @returns jQuery对象
         * @example
         * var selectedIndexed = $('.selector').omGridExtend('getChecks');
         * var selectedRecords = $('.selector').omGridExtend('getChecks',true);
         * 
         */
		getChecks : function(needRecords){
			var self = this , checks = $('span.selected',this.tbody),result = [];
			if(needRecords){
				var rows = this.pageData.data.rows;
				checks.each(function(i){
					var tr = $(this).parents('tr.om-grid-row');
					result[i] = rows[tr.attr('rowindex')];
				});
			}else{
				checks.each(function(i){
					var tr = $(this).parents('tr.om-grid-row');
					result[i] = tr.attr('rowindex');
				});
			}

			return result;
		},
		/**
         * 选择行。<b>注意:传入的参数是序号(第一行是0第二行是1)数组(比如[0,1]表示选择第一行和第二行);要想清除所有选择,请使用空数组[]作为参数;只能传入序号数组,如果要做复杂的选择算法,请先在其它地方算好序号数组后后调用此方法;此方法会清除其它选择状态,如选择第1,2行然后setSelections([3])最后结果中只有第3行,如setSelections([3,4]);setSelections([5,6])后只会选择5,6两行</b>。
         * @name omGrid#setChecks
         * @function
         * @param indexes 序号(第一行是0第二行是1)数组。
         * @returns jQuery对象
         * @example
         * //选择表格中第二行、第三行、第五行
         * $('.selector').omGridExtend('setChecks',[1,2,4]);
         * 
         */
		setChecks : function(indexes){
			var self = this ; 
			if(!$.isArray(indexes)){
                indexes=[indexes];
            }

			$(indexes).each(function(){
				self._rowCheck(this);
			});

		}
    });
})(jQuery);

?使用的时候只要引入这个文件即可,此外还有附件里面的css和image ,附件里面的css和image分别覆盖原来的css和image即可,不然可能图片显示不出来

页面使用时的代码如下:

?

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>基本功能</title>
    <script type="text/javascript" src="../../jquery.js"></script>
    <script src="../../ui/jquery.ui.core.js"></script>
    <script src="../../ui/jquery.ui.widget.js"></script>
    <script src="../../ui/jquery.ui.mouse.js"></script>
    <script src="../../ui/jquery.ui.resizable.js"></script>
    <script type="text/javascript" src="../../ui/om-grid.js"></script>
	<script type="text/javascript" src="../../ui/om-grid-extend.js"></script>
    <link rel="stylesheet" type="text/css" href="../../themes/default/om-all.css" />
    <link rel="stylesheet" type="text/css" href="../common/css/demo.css" />
    <!-- view_source_begin -->
    <script type="text/javascript">
        $(document).ready(function() {
            $('#grid').omGridExtend({
				//是否显示详情
				showDetail:true,
				singleSelect:false,
                dataSource : 'griddata.do?method=fast',
                colModel : [ {header : 'ID', name : 'id', width : 100, align : 'center'}, 
                             {header : '地区', name : 'city', width : 120, align : 'center'}, 
                             {header : '地址', name : 'address', align : 'center', width : 'autoExpand'} ],
				onRowClick:function(index,row){
					
				},
				onRowSelect:function(){
					
				},
				onRowDeselect:function(){
					
				},
				//当详情展开的时候调用的回调
				onDetailExpand:function(rowData,row){
					
				},
				//初始化的时候以哪一个字段排序
				sortName:'address',
				//排序方式
				sortOrder:'desc',
				method:'get',
				onRowCheck:function(index){
					
				}
            });
        });
    </script>
    <!-- view_source_end -->
</head>
<body>
    <!-- view_source_begin -->
    <table id="grid"></table>
    <!-- view_source_end -->
    <input type = "button" id = "get" value ="get" >
    <div id="view-desc">
        设置dataSource和colModel属性,在table元素中渲染表格。
    </div>
</body>
</html>
1 楼 mfkvfn 2012-02-02  
这样做比较好。其实排序功能是比较常见的,之所以我们没把排序做到omGrid中去,是因为我们设计时这个omGrid是一个基础组件,将作为其它grid组件的基类。而有些grid是不可以排序的,比如treeGrid(演示例见http://demo.operamasks.org/bpdemos/index.faces?userdata=treeGrid)、分组统计表格(演示示例见http://demo.operamasks.org/bpdemos/index.faces?userdata=groupSummaryGrid)、统计表格(演示示例见http://demo.operamasks.org/bpdemos/index.faces?userdata=pivotGrid)。所以如果基类有这些功能的话,子类把它去掉不好。

所以设计时基类只有基本功能(所有子类都有的),其它功能由子类(或者插件)来提供。
像你做的可展开表格的效果与http://demo.operamasks.org/bpdemos/index.faces?userdata=expanderGridPlug是一样的。

整个组件的体系与继承机制我们也已经讨论过了,直接的继承并不好,比如A的两个子类A1和A2,A1提供功能1,A2提供功能2,那么如果我同时要A1和A2功能怎么办?再做一个A3同时提供功能1和功能2?那这样功能一多就会产生组合爆炸了。所以我们推荐以插件的机制来做,就类似于《设计模式》中的“Decorator装饰模式”模式这种。
2 楼 mfkvfn 2012-02-02  
1.0之后的版本我们会陆续做一些grid或其它组件的插件(或子类)。

你做的这些功能值得参考。我们正在考虑一种贡献机制,大致是“贡献者自己完成基本功能,然后申请合并到主干,我们进行审核,如果采纳了,就将代码合并进来,由我们进行维护,并在文档显要位置标注贡献者的名字。如果某贡献者多次贡献重要代码,我们将把贡献者加入开发人员组中,与我们开发人员享有同等权利(可以直接提交代码到主干,可以参与Bug修复、测试、发版、文档等工作,可以收到日常工作邮件,可以查看日常内部交流资料等)”。具体细节还在制定中。近期可能会把代码托管到GitHub上,然后采用这种贡献机制。
  相关解决方案