HT for Web 表格组件手册

索引


概述

HT for Web提供了表格组件类ht.widget.TableView,用于显示DataModel数据容器中Data类型对象的属性信息,支持排序和过滤等功能。

通过tableView = new ht.widget.TableView(dataModel);初始化构建一个表格组件对象,dataModel参数为表格组件绑定的数据模型, 该模型为空时表格组件构造函数内部将创建一个新的数据模型进行绑定。

表格组件的getColumnModel()函数返回列模型对象,其本质也是个DataModel类型对象,只不过该对象是仅用于添加ht.Column类型对象, ht.Column类型的父类为ht.Data,增加了和属性定义相关的函数接口。

因此用户所需要做的工作是,根据要显示的属性信息构建出ht.Column对象,然后添加到tableView.getColumnModel()函数返回的列模型, 这样tableView.getDataModel()的数据模型中的Data的相关属性信息,就会根据tableView.getColumnModel()上存储的 ht.Column对象的配置进行显示。

ht.Column列类继承于ht.Data,不可设置父子关系,具备以下属性函数:

枚举是较为常见的属性编辑选择应用,在编辑时候呈现下拉列表,因此ht对枚举类型属性考虑了很多应用场景,setEnum(params)函数即可设置单个的json参数, 也可以设置逐个的参数信息setEnum(enumValues, enumLabels, enumIcons, enumEditable, enumStrict), 以下为常见的案例:

ht内部会自动检测用户是否引入了表单插件,若引入了表单插件的ht.widget.ComboBox组件则采用之作为编辑器, 否则采用原生htmlselect组件,由于原生htmlselect下拉组件只文本显示,因此上面的很多参数仅对ht.widget.ComboBox组件起作用。

表单插件中的滑动条ht.widget.Slider也是较为常见和易用的编辑组件, 为此ht也增加了该类型相应列属性的设置,通过getSlider()setSlider(parmas)可指定该列在编辑状态呈现的滑动条信息。

上列中告警级别信息存于Data对象的attr类型的属性alarmSeverity中,第一列设置了setSortFunc的排序函数, 实现Cleared告警级别置顶,其他告警级值越高排在越上层的效果,同时调用了tableView.setSortColumn(column)指定了当前排序列。

var column = new ht.Column();
column.setName("alarmSeverity");
column.setAccessType('attr');
column.setSortFunc(function(v1, v2, d1, d2){
    if(v1 === v2){
        return 0;
    }
    // keep 'Cleared' on top
    if(v1 === 0){
        return -1;
    }
    if(v2 === 0){
        return 1;
    }
    // compare value
    if(v1 > v2){
        return -1;
    }else{
        return 1;
    }                  
});
columnModel.add(column);                
tableView.setSortColumn(column);

第二列重载了column.getValue自定义了获取值的方式,根据attr类型的属性alarmSeverity值,通过map对象的 配置找到对应的告警级别颜色。通过设置valueTypecolor类型后,ht自动会用填充满单元格背景色的方式渲染该属性。

column = new ht.Column();
column.setValueType('color');
column.getValue = function(data){
    var alarmSeverity = data.getAttr('alarmSeverity'),
        color = map[alarmSeverity].color;
    return tableView.isSelected(data) ? ht.Default.darker(color) : color;
};
columnModel.add(column);

第三列重载了column.drawCell自定义单元格渲染效果,通过tableView.getRowIndex(data)返回data对象所在行索引, 将索引信息通过ht.Default.drawText绘制在单元格中间,同时根据索引的奇偶绘制不同的行背景色。

column = new ht.Column();             
column.drawCell = function (g, data, selected, column, x, y, w, h, tableView) {
    var index = tableView.getRowIndex(data);

    // draw background
    var color = index % 2 === 0 ? '#ECF0F1' : '#3498DB';
    g.fillStyle = selected ? ht.Default.darker(color) : color;
    g.beginPath();
    g.rect(x, y, w, h);
    g.fill();

    // draw label
    color = selected ? 'white' : 'black';
    ht.Default.drawText(g, 'row ' + index, null, color, x, y, w, h, 'center');
};
columnModel.add(column);

第四列重载了column.drawCell自定义单元格渲染效果,根据attr类型的属性alarmSeverity值, 通过map对象的配置找到对应的告警级别名字和颜色信息进行渲染绘制。

column = new ht.Column();
column.setWidth(200);    
column.drawCell = function (g, data, selected, column, x, y, w, h, tableView) {
    var alarmSeverity = data.getAttr('alarmSeverity'),
        info = map[alarmSeverity],
        color = info.color;

    // draw background                    
    g.fillStyle = selected ? ht.Default.darker(color) : color;
    g.beginPath();
    g.rect(x, y, w, h);
    g.fill();

    // draw label     
    color = selected ? 'white' : 'black';
    ht.Default.drawText(g, info.name, null, color, x, y, w, h, 'center');
};
columnModel.add(column);

表头组件

ht.widget.TableHeader表头组件常与TableViewTreeTableView结合呈现Column信息, 并提供Column的正反排序切换、列宽大小拉伸,以及列顺序位置变化等交互操作功能。

通过tableHeader = new ht.widget.TableHeader(tableView/treeTableView);初始化构建一个表头组件对象, 可传入tableViewtreeTableView的表格组件对象进行绑定,tableHeader将自动监听tableView.getColumnModel()的列模型, 当用户点击排序、列宽变化以及列顺序变化等操作,都将相应的修改到列模型的Column属性上,用户直接通过API修改Column属性时表头组件也会自动刷新。

以上例子构建了两个ht.widget.TableHeader表头组件,他们绑定同一个tableView对象,因此两个表头可实现交互同步的效果。

tableHeader1 = new ht.widget.TableHeader(tableView);
tableHeader2 = new ht.widget.TableHeader(tableView);

borderPane = new ht.widget.BorderPane();
borderPane.setTopView(tableHeader1);
borderPane.setCenterView(tableView);
borderPane.setBottomView(tableHeader2);

TableView提供了addColumnssetColumns的函数,可通过json格式较容易的批量添加Column列,上例采用了如下的代码方式:

tableView.addColumns([
    {
        displayName: 'Severity',
        name: 'alarmSeverity',
        accessType: 'attr',
        sortOrder: 'desc',
        tag: 'sortableColumn',
        sortFunc: function(v1, v2, d1, d2){
            if(v1 === v2){
                return 0;
            }
            // keep 'Cleared' on top
            if(v1 === 0){
                return -1;
            }
            if(v2 === 0){
                return 1;
            }
            // compare value
            if(v1 > v2){
                return -1;
            }else{
                return 1;
            }                  
        }
    },
    // ...                           
]);

例子中只有第一列允许排序,其他列的sortable属性都设置成了false,为了初始化显示就对该列进行排序,代码中特意为第一列设置了tag标识, 通过tableView.getColumnModel().getDataByTag('sortableColumn')查找到该列对象。

tableView.setSortColumn(tableView.getColumnModel().getDataByTag('sortableColumn'));

注意tableView.getColumnModel()也就是DataModelColumn也是继承于Data类型,因此具备tag等数据容器的相关操作。

表格面板

上节例子通过BorderPane容器组合了TableViewTableHeader对象,大部分情况这两个对象是需要一体化使用, 为此HT提供了ht.widget.TablePaneht.widget.TreeTablePane,这两个组件分别内置构建了TableViewTreeTableView对象, 同时也都内置构建了一个TableHeader对象,界面上TableHeader组件呈现于上方,TableViewTreeTableView组件呈现于下方。

ht.widget.TablePaneht.widget.TreeTablePane构造函数可分别传入TableViewTreeTableView对象, 若未传入则内部自动生成一个TableViewTreeTableView对象,其他常用函数如下:

以上例子中继承于ht.Column扩展了com.hightopo.LanguageColumncom.hightopo.LanguageColumn这两个列类型, 主要目的是在构造函数中初始化必要的参数配置信息,这样可以避免在项目比较大,同类型列需要多处定义时重复配置信息过多, 并有利于参数信息的统一修改。

com = {};
com.hightopo = {}; 

var LanguageColumn = com.hightopo.LanguageColumn = function(){
    LanguageColumn.superClass.constructor.call(this);                    
    this.setName('Language');
    this.setAccessType('attr');
    this.setEnum({values: [1, 2, 3, 4, 5, 6], labels: ['C', 'C++', 'Java', 'JS', 'AS', 'C#']});
    this.setEditable(true);
    this.setSortFunc(function(v1, v2, d1, d2){
        if(v1 === v2){
            return 0;
        }
        if(v1 === 2){
            return 1;
        }
        if(v2 === 2){
            return -1;
        }
        if(v1 === 5){
            return -1;
        }
        if(v2 === 5){
            return 1;
        }
        return v1 - v2;
    });                    
}; 
ht.Default.def('com.hightopo.LanguageColumn', ht.Column, {
});     

var SexColumn = com.hightopo.SexColumn = function(){
    SexColumn.superClass.constructor.call(this);                    
    this.setName('Sex');
    this.setAccessType('attr');
    this.setEnum(['Male', 'Female']);                   
    this.setEditable(true);                    
}; 
ht.Default.def('com.hightopo.SexColumn', ht.Column, {
});  

通过以上的类封装,配置列属性时就减少了很多参数,以下代码需注意className的关键字, ht会根据这个className特殊关键字信息来替换默认的ht.Column类型, 同理属性组件也会通过className特殊关键字信息来替换默认的ht.Property类型。

tablePane.addColumns([
    {
        name: 'id',
        width: 60,
        tag: 'id'
    },
    {
        className: 'com.hightopo.LanguageColumn',
        width: 100,
        tag: 'language'
    },
    {
        className: 'com.hightopo.SexColumn',
        width: 100,
        tag: 'sex'
    }
]);

propertyView.addProperties([
    {
        name: 'id'
    },
    {
        className: 'com.hightopo.LanguageProperty',
        editable: true
    },
    {
        className: 'com.hightopo.SexProperty',
        editable: true
    }                    
]);

通过tablePane.getTableHeader().setResizable(false)将表头被设置成不可改变列宽度:

tablePane.getTableHeader().setResizable(false);

通过tablePane.addViewListener监听tablePane的组件事件,当监听到beginValidate事件触发时, 根据当前组件宽度,对每个列宽进行分配,在定义column时设置了tag参数, 因此通过columnModel.getDataByTag('language')可找到对应的列:

tablePane.addViewListener(function(e){
    if(e.kind === 'beginValidate'){
        var columnModel = tablePane.getColumnModel(),
            width = tablePane.getWidth();
        columnModel.getDataByTag('id').setWidth(width * 0.2);
        columnModel.getDataByTag('language').setWidth(width * 0.4);
        columnModel.getDataByTag('sex').setWidth(width * 0.4);                        
    }
});

表格组件

表格组件类ht.widget.TableView主要可配置属性和函数如下:

以下为getCurrentSortFunc函数默认实现,TableViewsortFunc回调时的两个参数分别是d1d2即两个不同的Data对象, ColumnsortFunc回调时传入的是v1,v2,d1,d2四个参数,也就是头两个参数分别为Data对象对应列的值。

getCurrentSortFunc: function () {
    var column = this._sortColumn;
    if (column && column.isSortable()) {
        var func = column.getSortFunc(),
                tableView = this,
                order = 'asc' === column.getSortOrder() ? 1 : -1;            
        if (!func) {
            func = ht.Default.sortFunc;
        }
        return function (d1, d2) {
            var v1 = tableView.getValue(d1, column),
                v2 = tableView.getValue(d2, column);
            return func.call(tableView, v1, v2, d1, d2) * order;
        };
    }
    return this._sortFunc;
}

以下代码通过tableHeader.getView().style获取表头的底层div组件,采用css的设置以repeat-x方式平铺了渐进色的背景。

tableHeader = tablePane.getTableHeader();
tableHeader.getView().style.background = 'url(images/header.png) repeat-x';

以下代码重载了drawRowBackground函数,绘制了表格行交替斑马线的效果。

tableView.drawRowBackground = function(g, data, selected, x, y, width, height){
    if(tableView.isSelected(data)){
        g.fillStyle = '#87A6CB';
    }
    else if(tableView.getRowIndex(data) % 2 === 0){
        g.fillStyle = '#F1F4F7';
    }
    else{
        g.fillStyle = '#FAFAFA';
    }
    g.beginPath();
    g.rect(x, y, width, height);
    g.fill();
};

以下代码设置了可见过滤器,过滤器根据toolbar工具条的元素选中状态决定Data对象是否可见,由于过滤器只设置一次, 过滤规则随着工具条状态的变化而变化,但工具条元素的状态变化并未派发任何任何事件,因此工具条按钮的action都显示 调用了tableView.invalidateModel();通知表格进行数据重新加载更新。

tableView.setVisibleFunc(function(data){                    
    var nation = data.a('nation'),
        sex = data.a('sex');                    
    // filter nation 
    if(!toolbar.getItemById('nation-all').selected){
        if(toolbar.getItemById('uk').selected && nation !== 0){
            return false;
        }
        if(toolbar.getItemById('usa').selected && nation !== 1){
            return false;
        }
        if(toolbar.getItemById('mexico').selected && nation !== 2){
            return false;
        }
    } 
    // filter sex
    if(!toolbar.getItemById('sex-all').selected){
        if(toolbar.getItemById('man').selected && sex !== 0){
            return false;
        }
        if(toolbar.getItemById('woman').selected && sex !== 1){
            return false;
        }
    }
    return true;
});

以下代码在添加的列中重载了drawCell函数,通过ht.Default.drawStretchImage函数在单元格中心位置绘制了对应的图标, drawStretchImage函数的第三个参数可传入filluniformcenterUniform的类型。

tablePane.addColumns([
    {
        name: 'index',
        displayName: 'Index',
        accessType: 'attr',
        align: 'center'
    },
    {
        name: 'nation',
        displayName: 'Nation',
        accessType: 'attr',
        align: 'center',
        drawCell: function (g, data, selected, column, x, y, w, h, view) {
            var image = ht.Default.getImage('images/' + nations[data.a('nation')] + '.png');
            ht.Default.drawStretchImage(g, image, 'centerUniform', x, y, w, h);
        }

    },
    {
        name: 'sex',
        displayName: 'Sex',
        accessType: 'attr',
        align: 'center',
        drawCell: function (g, data, selected, column, x, y, w, h, view) {
            var image = ht.Default.getImage('images/' + sexs[data.a('sex')] + '.png');
            ht.Default.drawStretchImage(g, image, 'centerUniform', x, y, w, h);
        }
    }
]);

欢迎交流 service@hightopo.com