如何在gridx的cell中显示控件?

在gridx的列定义中,一个decorator方法可以被用来在cell中显示任何HTML/CSS. 但是有时候这稍显不够。当cell中需要放入复杂的控件时,从decorator方法中返回的纯string不再有效。所以gridx/modules/CellWidget模块在这是显示了作用。


如何在gridx的cell中显示控件?_第1张图片


为何是CellWidget


CellWidget模块通过grid的body模块中的onAfterRow事件将widget附加到cell节点中。其中的关键是如何才能高效地完成这个工作。

Grid的body随着时间不断刷新,刷新的原因可以是排序,过滤,分页,虚拟滚动,改变值,展开树节点,等等。每次body被刷新,行被重新,并且onAfterRow事件将会被处罚。如果我们总是在onAfterRow事件中创建新的widget,那将会非常浪费。如果你点击header两次为了要是的一列降序排序,许多widget将会被立刻摧毁。所以一些缓存机制必须被建立。第一个方法是为在每个cell中使用一个widget,无论被刷新多少次。这解决了一些刷新问题,但是如果我们有100000行,这仍然不高效。试想如果一个用户慢慢地滚动这个巨大的grid来看每一行,最终将会有多少widget被创建!

所以CellWidget的目的是创建尽可能少的控件。方法是使用set('value',...)方法在row之间重用控件。


如何使用CellWidget


在控件上设置值通常比创建一个新控件快。这是一个好方法,但是需要用户意识到重用这件事情。让我们首先看一下如何使用CellWidget模块:

var grid = new Grid({
    cacheClass: 'gridx/core/model/cache/Async',
    store: someStore,
    structure: [
        { id: 'progress', field: 'progress', name: 'Install Progress',
            widgetsInCell: true,
            decorator: function(){
                return "<div data-dojo-type='dijit.ProgressBar' data-dojo-props='maximum: 1' " + 
                    "class='gridxHasGridCellValue' style='width: 100%;'></div>";
            }
        }
    ],
    modules: [
        "gridx/modules/CellWidget"
    ]
});

  • 首先,不要忘记载入所有必要的资源: store, cache, 模块和你想要在cell中展示的widget。如果你仍然在创建一个gridx的时候遇到麻烦,请阅读这篇教程.
  • 第二,在你想要显示widgets的列中将widgetsInCell 属性设置为true。CellWidget模块将只会在这些列上有效。
  • 第三,在你的设置widgetsInCell的列中提供decorator 方法。


从decorator方法中返回模板字符串


这里你将看到第一个小技巧:decorator并没有任何的参数。这并不是当widgetsInCell 为false的情况。通常当前的cell数据会被传入,和row Id和Index一起。如果某些特定的cell数据被传入,我们将如何重用他们?所以这个template string不能包含任何特定的行信息。在上面的示例中,一个dijit/ProgressBar被放进template中,他的参数可以在data-dojo-props中被加入。注意这个gridxhasGridCellValue class,这是关于widgets被重用的第二个技巧。CellWidget模块将会在渲染每一行时自动查找所有含有这个class的widget,并且调用set('value')在widget中设置合适的值。


在setCellValue中设置widgets


在上面的例子中,在调用 widgetset('value')之前不需要转换数据,因此事情显得比较简单。在设置widget值的时候,如果你想做一些有趣的事情,该怎么办? setCellValue方法在这个时候派上了用处。

{ id: 'progress', field: 'progress', name: 'Install Progress',
    widgetsInCell: true,
    decorator: function(){
        return "<div data-dojo-type='dijit.ProgressBar' data-dojo-props='maximum: 1' " +
            "data-dojo-attach-point='progBar' style='width: 100%;'></div>";
    },
    setCellValue: function(gridData, storeData, cellWidget){
        var data = doSomethingIntersting(gridData);
        cellWidget.progBar.set('value', data);
        // cellWidget.cell give you full access to everything you want.
        var rowIndex = cellWidget.cell.row.index();
    }
}

setCellValue方法会在每一行被render的时候被调用。当这个方法被调用时,widget已经被创建并且你能完全控制widget。你可以在widgets上设置值,改变css,或者操纵dom节点并且添加事件。这个方法中的第三个参数'cellWidget'引用了cell widget本身,这是拥有从'decorater'方法中返回的template string的widget,所以你可以访问任何的在控件中定义的“dojo attach point”。第一和第二个参数是当前cell的grid data与store data。他们只有当'formatter'方法被提供时才会有差别。你可以通过cellWidget.cell来获取当前的cell,从中你可以获取任何你需要的东西。


如何处理widget事件

但是请记住widgets("cell widget"作为一个整体)是在不同的行之间被重用。所以如果你在setCellValue方法中绑定了一些事件或者改变了一些dom节点的时候,这些改变将会在被其他行重用时被保留。如果你一直连接事件而不断开事件,将会有巨大的内存泄漏风险。所以合适的方法是在setCellValue中连接并且断开事件。例如


{ id: 'progress', field: 'progress', name: 'Install Progress',
    widgetsInCell: true,
    decorator: function(){
        return "<button data-dojo-type='dijit.form.Button' data-dojo-attach-point='btn'></button>";
    },
    setCellValue: function(gridData, storeData, cellWidget){
        cellWidget.btn.set('label', gridData);
        if(cellWidget.btn._cnnt){
            // Remove previously connected events to avoid memory leak.
            cellWidget.btn._cnnt.remove();
        }
        cellWidget.btn._cnnt = dojo.connect(cellWidget.btn, 'onClick', function(e){
            alert(gridData);
            // do your job here......
        });
    }
}

我承认,这看起来不怎么直观。所以在gridx 1.2中一个新的方法(回调)在会被引入以使得这个工作更加简便:

{ id: 'progress', field: 'progress', name: 'Install Progress',
    widgetsInCell: true,
    decorator: function(){
        return "<button data-dojo-type='dijit.form.Button' data-dojo-attach-point='btn'></button>";
    },
    setCellValue: function(gridData, storeData, cellWidget){
        cellWidget.btn.set('label', gridData);
    },
    getCellWidgetConnects: function(cellWidget, cell){
        // return an array of connection arguments
        return [
            [cellWidget.btn, 'onClick', function(e){
                alert(cell.data());
                // do your job here.....
            }]
        ];
    }
}

有了这个getCellWidgetConnects,gridx可以为你管理事件链接(句柄)。连接和断开都会被自动执行。


其他高级的回调函数

初次之外,gridx 1.2添加了另外2个方法来为你的工作提供更加有意义的命名:

{ id: 'progress', field: 'progress', name: 'Install Progress',
    widgetsInCell: true,
    decorator: function(){
        return "<button data-dojo-type='dijit.form.Button' data-dojo-attach-point='btn'></button>";
    },
    setCellValue: function(gridData, storeData, cellWidget){
        cellWidget.btn.set('label', gridData);
    },
    getCellWidgetConnects: function(cellWidget, cell){
        // return an array of connection arguments
        return [
            [cellWidget.btn, 'onClick', function(e){
                alert(cell.data());
                // do your job here.....
            }]
        ];
    },
    initializeCellWidget: function(cellWidget, cell){
        // create extra widgets or manipulate dom nodes that depends on current cell context.
        cellWidget.anotherButton = new Button({...});
        cellWidget.domNode.append(cellWidget.anotherButton.domNode);
    },
    uninitializeCellWidget: function(cellWidget, cell){
        // don't forget to undo the changes you made in initializeCellWidget, so that it can be reused among different rows.
        cellWidget.anotherButton.destroy();
    }
}

如你所见,事实上你可以在setCellValue方法中做这些事情。这个新方法只是提供了更多的语义,使你的代码更加易于阅读并且省去了你添加额外的注释的功夫。

创建控件的编程方式


如果你曾经用过DataGrid你可能会对于在formatter方法中返回widget的方式感到熟悉,并且对于写template string的方式感到变扭。因此你可以使用onCellWidgetCreated事件,甚至省略“decorator方法(gridx 1.2之后):

{ id: 'progress', field: 'progress', name: 'Install Progress',
    widgetsInCell: true,
    onCellWidgetCreated: function(cellWidget, column){
        var btn = new Button({...});
        btn.placeAt(cellWidget.domNode);
    }
}

onCellWidgetCreated 只当一个新的cell widget被创建被调用。它不会在widget重用时被调用。跟decorator方法相同,不应该在此处使用特定行的信息。

结论

以下是如何在gridx的cell中显示widget的结论:

  1. 为grid引入gridx/moduels/CellWidget模块
  2. 在列设置中将widgetsInCell设置为true
  3. decorator方法中返回模板string,或者在onCellWidgetCreated事件处理函数中创建控件,或者两者皆执行
  4. setCellValue方法中设置widget的值(可选)
  5. getCellWidgetConnects中提供事件连接(可选,gridx 1.2之后
  6. initialCellWidgetUninitialCellWidget中做额外的操作(可选,gridx 1.2之后)




你可能感兴趣的:(如何在gridx的cell中显示控件?)