原创英文版链接:https://github.com/evanhw/gridx/wiki/Introduction-to-Gridx
虽然同样都是基于Dojo store, 但与DataGrid/EnhancedGrid相比,Gridx有一套完全不同的架构。它有以下特点:
- Gridx采用了一套与UI无关的内核来处理所有的表格数据的逻辑操作。
- Gridx采用了一套灵活的模块化系统,它与基于plugin的EnhancedGrid架构不同,这种模块化架构不仅能提配置各种表格特性的灵活性,并且当一些功能不使用时,能够减少运行代码的大小。
- Gridx开发了一套简洁直接的API,使得各模块之间实现真正的松耦合。
本文将简要介绍如何使用Gridx进行开发。现在Gridx仍处于开发阶段,一些API和实现的细节仍有可能调整。但它的基本用法和架构已经稳定。感兴趣的童鞋不妨一试。
1. 创建
假设我们有如下HTML页面:
<html> <head> <title>Gridx Demo</title> <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true"></script> </head> <body> <!-- We'd like to show a grid here --> <div id="gridNode" style="width: 400px; height: 300px;"></div> </body> </html>
首先,要引入Gridx的CSS:
<link rel="stylesheet" href="gridx/resources/claro/Gridx.css" />
目前Gridx只支持Claro主题,如果要在RTL模式下运行,请用如下代码来引入:
<link rel="stylesheet" href="gridx/resources/claro/Gridx_rtl.css" />
我们必须“请求require” 的JavaScript 模块是:
- 一种Store类型,例如:dojo/store/Memory
- gridx/Grid
- gridx/core/model/cache/Sync (用于客户端store) or gridx/core/model/cache/Async (用于服务器端 store)
假设我们要创建基于dojo/store/Memory的grid,那么我们需要如下代码:
require([ 'dojo/store/Memory','gridx/Grid','gridx/core/model/cache/Sync' ],function(Store,Grid,Cache){ var store = new Store({ data: [ {id: 1,title: 'Hey There',artist: 'Bette Midler'},{id: 2,title: 'Love or Confusion',artist: 'Jimi Hendrix'},{id: 3,title: 'Sugar Street',artist: 'Andy Narell'} ] }); ...... });
与DataGrid/EnhancedGrid类似,我们需要定义列结构:
var columns = [ {field: 'id',name: 'Identity'},{field: 'title',name: 'Title'},{field: 'artist',name: 'Artist'} ];
OK,万事俱备,现在我们可以创建一个简单的grid了:
var grid = new Grid({ cacheClass: Cache,store: store,structure: columns },'gridNode'); //Assume we have a node with id 'gridNode' grid.startup();
Gridx继承于dijit._WidgetBase,因此它支持所有widget的基本特性。
以上创建的grid是一个最基本但可用的grid。我们可以灵活的给它加上很多模块,并且这些模块独立工作,它们之间不会相互冲突。下面一节我们将介绍如何实现这一点。
2. 基本API和API对象
Gridx引入了如下API对象:rows,columns,和cells,可以通过这些对象来操作API。
例如我们可以用如下代码来获得第2行的id:
var id = grid.row(1).id;获得位于第1行第2列格子的数据:
var data = grid.cell(0,1).data();获得第3列的name属性:
var title = grid.column(2).name();
以上方法中的grid.row(),grid.cell() 和 grid.column()就是我们所说的API对象。它们只是一套API的容器,本身是没有状态的。因此,我们如果想获得一个cell对象,可用如下代码:
var cell = grid.cell(0,0); var data1 = cell.data();然后我们改变此cell中的data,再调用:
var data2 = cell.data();则data2中就得到更新过的cell data,而不是data1的值。
如果想要获得列名组成的数组,我们可以使用一个更好的API:
var names = grid.columns().map(function(col){ return col.name(); });columns()和rows()方法返回的是API对象数组。这两个方法同样接受指定范围的参数。例如,如果你只想获得前3列的列名,就可以写成:
grid.columns(0,3);如果需要从第2列开始的两列列名,则可以写成:
grid.columns(1,2);获得从第3列开始的后面所有列名:
grid.columns(2);返回的数组可以用各种非常有用的数组函数才操作,例如map,filter,forEach,some 以及 every。
我们可以使用API对象来调用模块提供的更多方法。例如,如果加载了sort模块,我们可以这样写:
grid.column(0).sort();下面一些其它常用的API方法:
grid.setColumns(columnStructure); //Reset the column structure grid.setStore(store); //Reset store grid.columnCount(); //Faster than grid.columns().length; grid.rowCount(); //Faster than grid.rows().length; grid.resize(); //This is what layout widgets (containers) need.模块提供了模块API,它们在底层整合,因此不必担心模块之间会相互影响造成冲突。下一节我们将具体介绍更多关于模块API的内容。
3. 模块
几乎所有的Gridx UI特性都是以模块的方式实现,包括表头,表格体(所有行), 滚动条(横向和纵向滚动条)等。不过这些是核心模块,缺省状态下是自动加载的。非核心模块与EnhancedGrid的插件“plugin”类似,可以根据需要选择要加载的模块。
理论上说,一个Gridx模块可以只是一个dojo类,并可以放在任何地方。但通常情况下,Gridx模块继承自gridx/core/_Module 并且放在gridx/modules目录下。如果要在一个grid实例中添加一个模块,首先我们需要“请求”该模块的源代码,然后在创建grid的时候声明之。
require([ 'dojo/store/Memory','gridx/core/model/cache/Sync','gridx/modules/SingleSort',//Require module source code 'gridx/modules/ColumnResizer' //Require module source code ],Cache,Sort,ColumnResizer){ ...... var grid = new Grid({ cacheClass: Cache,structure: columns,modules: [ Sort,//Declare modules in a grid instance ColumnResizer //Declare modules in a grid instance ] }); });为一个grid实例声明模块只需要将所有模块类放在一个数组中。这种声明方法比之EnhancedGrid的plugin更加简单明了。因为相对于隐式使用的类,例如string类,被“请求”的模块类(Sort和ColumnResizer) 是显式地使用的。但是当我们想要传递一些初始参数,则需写成:
modules: [ { moduleClass: ColumnResizer,detectWidth: 10 },...... ]如果你感觉这种写法不够酷,那么还有另外一种写法,将其声明成grid的参数
var grid = new Grid({ ...... modules: [ ColumnResizer ],columnResizerDetectWidth: 10 });如果一个模块参数声明成grid的参数,它必须以模块名开头,这样不同模块的参数才不会相互冲突。
Gridx模块被设计成可替换的。这意味着如果你不喜欢某一个模块,你完全可以实现你自己的模块,同时不需要太担心它是否会对grid的其他部分或者该模块之前的实现产生不可预料的影响。你只需要遵循该模块所有的API,这样你的实现就能很好的与其他模块无缝合作。这里的“API集合”是用模块名来标识的。是的,跟声明grid参数一样将模块名作为前缀。例如,模块 gridx/modules/select/Row/ 名为“selectRow”, 这意味着它实现了所有“selectRow”的API。现有另一模块 gridx/modules/extendedSelect/Row, 它也名为“selectRow”,这意味着该模块实现了同样的API集合。因此当另外一个模块依赖于行选择的特性,它只需要依赖于这个API集合,而不是某一个特定的实现。
有时候,模块也可能依赖于其他模块。例如,UI模块“paginationBar” 依赖于一个非UI模块“pagination”。 因此当声明PaginationBar模块时,我们也需要请求Paginatioin模块。但我们不需要在创建grid时声明Pagination模块,只是“请求”这个关联模块的源代码即可。如果没有请求这个关联模块,我们就会在控制台中得到如下出错信息,并且该grid很可能不会正确的显示。
Error: Forced/required Dependent Module 'pagination' is NOT Found for 'paginationBar'
这看起来似乎有点不方便。因为在EnhancedGrid中,所有关联的插件都是自动加载的。但为了提高灵活性,这种做法是不可避免的。因为在EnhancedGrid中,如果一个插件已经自动被其他插件请求了,我们就无法轻松的替换这个插件的实现。另一个好处是,这种做法可以减少一些不可见的源代码加载,从而提高效率。
几乎所有的模块都可以通过grid对象来直接访问。例如:
grid.select.row.selectById(rowId); grid.select.column.selectById(colId); grid.sort.sort(colId); grid.columnResizer.setWidth(colId,100);在这种方式下,每个模块都有自己的命名空间,因此他们不会相互冲突。
一个模块可以选择暴露哪些API给grid,已经暴露哪些API给API对象(row/column/cell)。请注意模块名只是暴露给grid的API的标识,这样就可以减少依赖性,并且提高代码的可维护性。
以下是目前支持的非核心模块列表。更多精彩细节我们会在将来的博文中介绍。
1. CellWidget 2. ColumnLock 3. ColumnResizer 4. Dod 5. Edit 6. Focus 7. IndirectSelect 8. Menu 9. NestedSort 10. Persist 11. Printer 12. RowHeader 13. RowLock 14. SingleSort 15. SummaryBar 16. TitleBar 17. Toolbar 18. Tree 19. VirtualVScroller 20. Filter 21. FilterBar 22. Pagination 23. PaginationBar 24. move.Row 25. move.Column 25. dnd.Row 26. dnd.Column 26. select.Row 27. select.Column 28. select.Cell 29. extendedSelect.Row 30. extendedSelect.Column 31. extendedSelect.Cell 32. exporter.CSV 33. exporter.Table