Wijmo 5

FlexGrid 101

这个页面展示了如何开始使用Wijmo FlexGrid控件。

入门

在JavaScript应用中开始使用FlexGrid的步骤:

  1. 添加Wijmo的引用。
  2. 添加标记作为Wijmo控件的宿主。
  3. 通过JavaScript初始化Wijmo控件。
  4. (可选)添加一些CSS来自定义FlexGrid控件的外观。

以下将会创建一个具有默认行为的FlexGrid,包括自动生成列,列排序,重新排序,编辑和剪贴板支持。

<!DOCTYPE html> <html> <head> <link rel="stylesheet" type="text/css" href="css/bootstrap.css"/> <link rel="stylesheet" type="text/css" href="css/wijmo.css" /> <link href="css/app.css" rel="stylesheet" type="text/css" /> <script src="scripts/wijmo.js" type="text/javascript"></script> <script src="scripts/wijmo.input.js" type="text/javascript"></script> <script src="scripts/wijmo.grid.js" type="text/javascript"></script> </head> <body> <!-- this is the grid --> <div id="gsFlexGrid"></div> </body> </html>
var countries = 'US,Germany,UK,Japan,Italy,Greece'.split(','); var data = []; for (var i = 0; i < count; i++) { data.push({ id: i, country: countries[i % countries.length], date: new Date(2014, i % 12, i % 28), amount: Math.random() * 10000, active: i % 4 === 0 }); } // create the grid and give it some data var grid = new wijmo.grid.FlexGrid('#gsFlexGrid'); grid.itemsSource = data;
/* set default grid style */ .wj-flexgrid { height: 300px; background-color: white; box-shadow: 4px 4px 10px 0px rgba(50, 50, 50, 0.75); margin-bottom: 12px; }

Result (live):

列定义

上面入门的样例没有定义任何列,所以FlexGrid会自动生成它们。

这个样例演示如何使用FlexGrid的initialize方法和columns集合直接定义列。

指定列允许你选择哪些列显示,以及按什么顺序。这也给了你对于每一列的宽度,标题,格式,对齐和其它属性的控制。

在这个样例中,我们使用星形来设置”Country”列的宽度。这告诉列通过拉伸来填充表格的可用宽度,因此没有空余的空间。 在”Revenue”列,我们设置格式属性为”n0”,导致数字是千分位分隔符,并且没有小数位数字。

<b>Initialize Method</b> <div id="cdInitMethod"></div> <b>Column Definitions</b> <div id="cdColsCollection"></div>
// create two grids and some data var fgInitMethod = new wijmo.grid.FlexGrid('#cdInitMethod'), fgColsCollection = new wijmo.grid.FlexGrid('#cdColsCollection'), cv = new wijmo.collections.CollectionView(data.getData(100)); // initialize one grid using 'initialize' method fgInitMethod.initialize({ autoGenerateColumns: false, columns: [ { header: 'Country', binding: 'country', width: '*' }, { header: 'Date', binding: 'date' }, { header: 'Revenue', binding: 'amount', format: 'n0' }, { header: 'Active', binding: 'active' }, ], itemsSource: cv }); // initialize the other grid using the columns collection fgColsCollection.autoGenerateColumns = false; fgColsCollection.itemsSource = cv; var c = new wijmo.grid.Column(); c.binding = 'country'; c.header = 'Country'; c.width = '*'; fgColsCollection.columns.push(c); c = new wijmo.grid.Column(); c.binding = 'date'; c.header = 'Date'; fgColsCollection.columns.push(c); c = new wijmo.grid.Column(); c.binding = 'amount'; c.header = 'Revenue'; c.format = 'n0'; fgColsCollection.columns.push(c); c = new wijmo.grid.Column(); c.binding = 'active'; c.header = 'Active'; fgColsCollection.columns.push(c);

Result (live):

Initialize Method
Columns Collection

选择模式

默认情况下,FlexGrid允许你使用鼠标或键盘选择一个范围的单元格,就像Excel做的那样,selectionMode属性允许你改变这一行为, 因此,你可以选择一行,一个范围内的行,不连续的行(如在一个列表框),一个单元格或者将选择完全禁用。

这个样例允许你从一个Wijmo菜单控件中选择selectionMode

<div id="smFlexGrid"></div> <select id="smMenu"> <option value="None">None</option> <option value="Cell">Cell</option> <option value="CellRange" selected>CellRange</option> <option value="Row">Row</option> <option value="RowRange">RowRange</option> <option value="ListBox">ListBox</option> </select>
// initialize grid and menu var grid = new wijmo.grid.FlexGrid('#smFlexGrid'), menu = new wijmo.input.Menu('#smMenu'), cv = new wijmo.collections.CollectionView(data.getData(100)); grid.itemsSource = cv; updateMenuHeader(); // update grid selection mode when an item is selected from the menu menu.itemClicked.addHandler(function (sender) { grid.selectionMode = sender.selectedValue; updateMenuHeader(); }); // update menu header to show current selection mode function updateMenuHeader() { menu.header = '<b>Selection Mode:</b> ' + menu.text; }

Result (live):

编辑

FlexGrid对快速编辑,单元格内部编辑具有内置的支持,正如你在Excel中。使用控制显示和编辑模式切换的Edit按钮来添加额外的列是没有必要的。

用户可以通过键入任何一个单元格来开始编辑。这样会使单元格处于快速编辑模式。在这种模式下,按下光标键会完成编辑并且选中一个不同的单元格。

另一种开始编辑的方法是用过按F2键或者双击一个单元格。这会是单元格处于完全编辑模式。在这种模式下,按下光标键会在单元格文本中移动光标。 为了完成编辑并且移动到下一个单元格,用户必须按下回车,Tab或者Esc键。

当完成编辑后,数据会自动转换为合适的类型。如果用户输入了无效的数据,编辑会被取消,原始数据会保留在原来的位置。

你可以通过设置表格,列或者行对象的isReadOnly属性来禁止对它们的编辑。在这个样例中,我们将ID列置为只读。

<div id="eFlexGrid"></div>
// create and initialize grid (editing is enabled by default) var grid = new wijmo.grid.FlexGrid('#eFlexGrid', { autoGenerateColumns: false, columns: [ { header: 'ID', binding: 'id', width: '*', isReadOnly: true }, // cannot edit { header: 'Country', binding: 'country' }, { header: 'Date', binding: 'date' }, { header: 'Revenue', binding: 'amount', format: 'n0' }, { header: 'Active', binding: 'active' }, ], itemsSource: data.getData(100) });

Result (live):

分组

FlexGrid支持分组,通过ICollectionView接口,与.NET中的相同。 为了达到分组的目的,向CollectionView.groupDescriptions属性增加一个或多个GroupDescription对象 并且确保表格的showGroups属性设置为true(默认属性)

GroupDescription对象是灵活的,允许你基于值或者分组函数来对数据进行分组。以下的这个样例按年份对时间分组,按返回的三个范围: 大于5000,500到5000,小于500来对总数分组,其它按值分组。通过菜单可以看到每个分组的效果。

观察”Revenue”列展示了行组的总和。我们是通过设置列的aggregate属性为"Sum"做到的。数据聚合会在你编辑列的值时自动更新。

<div id="gFlexGrid"></div> <select id="gMenu"> <option value="" selected>(no grouping)</option> <option value="country">Country</option> <option value="amount">Revenue</option> <option value="date">Date</option> <option value="country,date">Country and Date</option> <option value="country,amount">Country and Revenue</option> <option value="country,date,amount">Country, Date, and Revenue</option> </select>
// initialize grid and menu var grid = new wijmo.grid.FlexGrid('#gFlexGrid'), menu = new wijmo.input.Menu('#gMenu'), cv = new wijmo.collections.CollectionView(data.getData(100)); grid.initialize({ autoGenerateColumns: false, columns: [ { header: 'Country', binding: 'country', width: '*' }, { header: 'Date', binding: 'date' }, { header: 'Revenue', binding: 'amount', format: 'n0' } ], itemsSource: cv }); updateMenuHeader(); // handle the Menu control's selectedIndexChanged event menu.itemClicked.addHandler(function (sender) { var groupBy = sender.selectedValue; cv.groupDescriptions.clear(); if (groupBy) { var groupNames = groupBy.split(','); for (var i = 0; i < groupNames.length; i++) { var groupName = groupNames[i]; if (groupName == 'date') { // group dates by year var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName, function (item, prop) { return item.date.getFullYear(); }); cv.groupDescriptions.push(groupDesc); } else if (groupName == 'amount') { // group amounts in ranges var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName, function (item, prop) { return item.amount >= 5000 ? '> 5,000' : item.amount >= 500 ? '500 to 5,000' : '< 500'; }); cv.groupDescriptions.push(groupDesc); } else { // group everything else by value var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName); cv.groupDescriptions.push(groupDesc); } } } updateMenuHeader(); }); // show currently selected item function updateMenuHeader() { menu.header = '<b>Group By:</b> ' + menu.text; }

Result (live):

过滤

FlexGrid支持过滤,通过ICollectionView接口,与.NET中的相同。为了达到过滤的目的, 设置CollectionView.filter的属性为一个函数,由这个函数决定那个对象要留在视野中。

在这个样例中,我们创建了一个对于country的过滤,可以通过输入控件来获得过滤后得到的值。

<div id="fFlexGrid"></div> <div class="input-group"> <span class="input-group-addon"> <i class="glyphicon glyphicon-filter"></i> </span> <input id="fFilter" type="text" class="form-control" placeholder="Filter by Country..." /> </div>
// create grid, some data var grid = new wijmo.grid.FlexGrid('#fFlexGrid'), cv = new wijmo.collections.CollectionView(data.getData(100)), filterEl = document.getElementById('fFilter'), filterText = ''; // populate the grid with data grid.itemsSource = cv; // update grid when filter changes filterEl.addEventListener('input', function () { filterText = this.value.toLowerCase(); cv.refresh(); }); // CollectionView filter cv.filter = function (item) { return !filterText || item.country.toLowerCase().indexOf(filterText) > -1; };

Result (live):

分页

FlexGrid支持分页,通过IPagedCollectionView接口,与.NET中的几乎相同。 为了达到分页的目的,设置IPagedCollectionView.pageSize属性为你想要在每页上展示项目的数量,并且提供一个界面导航的UI。

在这个样例中,我们使用JavaScript在每页展示10个项目。我们增加了导航按钮,并且在按钮单击指令中调用了IPagedCollectionView方法。 注意,我们使用了pageIndexpageCount属性来展示当前页和总的页数。

<div id="pFlexGrid" style="height:auto"></div> <div class="btn-group" id="pPager"> <button type="button" class="btn btn-default" data-action="fast-backward" id="pfb"> <span class="glyphicon glyphicon-fast-backward"></span> </button> <button type="button" class="btn btn-default" data-action="step-backward" id="psb"> <span class="glyphicon glyphicon-step-backward"></span> </button> <button type="button" class="btn btn-default" disabled style="width:100px" data-action="none" id="pn"></button> <button type="button" class="btn btn-default" data-action="step-forward" id="psf"> <span class="glyphicon glyphicon-step-forward"></span> </button> <button type="button" class="btn btn-default" data-action="fast-forward" id="pff"> <span class="glyphicon glyphicon-fast-forward"></span> </button> </div>
// create a CollectionView, set the page size to 10, initialize pager var cv = new wijmo.collections.CollectionView(data.getData(100)), pagerButtons = Array.prototype.slice.call(document.querySelectorAll('#pPager button')); cv.pageSize = 10; // set collectionView's pageSize updatePager(); // show the data in a grid var grid = new wijmo.grid.FlexGrid('#pFlexGrid'); grid.itemsSource = cv; // update pager when user clicks a button pagerButtons.forEach(function(el) { el.addEventListener('click', function () { updatePager(this.getAttribute('data-action')); }); }); // disable/enable buttons and update display text for pager function updatePager(action) { // get buttons by id var display = document.getElementById('pn'), fb = document.getElementById('pfb'), sb = document.getElementById('psb'), sf = document.getElementById('psf'), ff = document.getElementById('pff'), enableBackwards = false, enableForwards = false; // handle pager operation based on button's attribute switch (action) { case 'fast-backward': cv.moveToFirstPage(); break; case 'step-backward': cv.moveToPreviousPage(); break; case 'step-forward': cv.moveToNextPage(); break; case 'fast-forward': cv.moveToLastPage(); break; } // update the pager text display.innerHTML = (cv.pageIndex + 1) + ' / ' + (cv.pageCount); // determine which pager buttons to enable/disable enableBackwards = cv.pageIndex <= 0; enableForwards = cv.pageIndex >= cv.pageCount - 1; // enable/disable pager buttons fb.disabled = enableBackwards; sb.disabled = enableBackwards; sf.disabled = enableForwards; ff.disabled = enableForwards; }

Result (live):

主-细节

ICollectionView接口对当前值有内置的支持,可以让你在FlexGrid中实现主-细节的场景。 你可以引用currentItem并且将它作为页面上任意元素的绑定源。

注意,当当前项更改时,你必须告知AngularJS。 要做到这个,向ICollectionView.currentChanged事件添加一个处理器,并且调用这个样例中显示为JS 选项卡的$scope.$apply

<div id="mdFlexGrid"></div> <dl class="dl-horizontal"> <dt>ID</dt> <dd id="mdCurId"></dd> <dt>Country</dt> <dd id="mdCurCountry"></dd> <dt>Date</dt> <dd id="mdCurDate"></dd> <dt>Revenue</dt> <dd id="mdCurRevenue"></dd> <dt>Active</dt> <dd id="mdCurActive"></dd> </dl>
// create a CollectionView to keep track of selection var cv = new wijmo.collections.CollectionView(data.getData(100)); // initialize details pane updateDetails(); // update details when current item changes cv.currentChanged.addHandler(function (sender, args) { updateDetails(); }); // create a grid to show/edit the data var grid = new wijmo.grid.FlexGrid('#mdFlexGrid', { autoGenerateColumns: false, columns: [ { header: 'Country', binding: 'country', width: '*' }, { header: 'Date', binding: 'date' } ], itemsSource: cv }); // update the details when the CollectionView's currentItem changes function updateDetails() { var item = cv.currentItem; document.getElementById('mdCurId').innerHTML = item.id; document.getElementById('mdCurCountry').innerHTML = item.country; document.getElementById('mdCurDate').innerHTML = wijmo.Globalize.format(item.date, 'd'); document.getElementById('mdCurRevenue').innerHTML = wijmo.Globalize.format(item.amount, 'c'); document.getElementById('mdCurActive').innerHTML = item.active; }

Result (live):

ID
Country
Date
Revenue
Active

条件样式

FlexGrid有一个itemFormatter属性,给予你对单元格内容完全的控制。

这个样例使用了一个JavaScript函数来创建值范围,返回命名的颜色。 然后我们在FlexGrid的itemFormatter属性调用这个函数,传递单元格的数据,以条件化地设置单元格的前景颜色。

<div id="csFlexGrid"></div>
// create grid, some data var grid = new wijmo.grid.FlexGrid('#csFlexGrid'), cv = new wijmo.collections.CollectionView(data.getData(100)); // initialize grid grid.initialize({ autoGenerateColumns: false, columns: [ { header: 'Country', binding: 'country', width: '*', isContentHtml: true, isReadOnly: true }, { header: 'Date', binding: 'date' }, { header: 'Revenue', binding: 'amount', format: 'n0' }, { header: 'Active', binding: 'active' }, ], itemsSource: cv, itemFormatter: function (panel, r, c, cell) { // we are only interested in regular (scrollable) cells if (wijmo.grid.CellType.Cell === panel.cellType) { // compute the cell color // (for all columns, since cells may be recycled) var color = ''; if (panel.columns[c].binding == 'amount') { var cellData = panel.getCellData(r, c); color = getAmountColor(cellData); } // always set the color cell.style.color = color; } } }); // get the color used to display an amount function getAmountColor(amount) { return amount < 500 ? 'red' : amount < 2500 ? 'black' : 'green'; }

Result (live):

主题

FlexGrid的外观是在CSS中定义的。除了默认的主题外,我们有十几个专业设计的主题,它们自定义了所有Wijmo控件的外观来达到一致的,有吸引力的效果。

你可以使用CSS自定义表格的外观。要做到这一点,从默认主题复制CSS规则到一个新的CSS文件并且修改你想要更改的样式属性。

在这个样例中,我们向表格元素添加了一个custom-flex-grid类, 定义了一些CSS规则为任何拥有custom-flex-grid类的表格来创建一个简单的“黑与白,无国界”主题。

<div id="tFlexGrid" class="custom-flex-grid"></div>
var grid = new wijmo.grid.FlexGrid('#tFlexGrid'); grid.itemsSource = data.getData(100);
.custom-flex-grid .wj-header.wj-cell { color: #fff; background-color: #000; border-bottom: solid 1px #404040; border-right: solid 1px #404040; font-weight: bold; } .custom-flex-grid .wj-cell { background-color: #fff; border: none; } .custom-flex-grid .wj-alt:not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: #fff; } .custom-flex-grid .wj-state-selected { background: #000; color: #fff; } .custom-flex-grid .wj-state-multi-selected { background: #222; color: #fff; }

Result (live):

树形和分层数据

除了分组,FlexGrid支持分层数据,即拥有一列子项的项目的数据。这种类型的分层机构是非常常见的,通常在树视图控件中显示。

要使用有分层数据源的FlexGrid,设置childItemsPath属性为包含子元素的数据元素的名字。表格会自动扫描数据,为你构建树。

<div id="tvFlexGrid" class="custom-flex-grid"></div>
// create the grid var grid = new wijmo.grid.FlexGrid('#tvFlexGrid'); // populate the grid and set childItemsPath to show data hierarchically grid.childItemsPath = 'items'; // initialize the grid to show hierarchical data grid.initialize({ autoGenerateColumns: false, columns: [ { binding: 'name', width: '*' }, { binding: 'length', width: 80, align: 'center' } ], itemsSource: data.treeData, // hierarchical data childItemsPath: 'items', // set hierarchy path allowResizing: wijmo.grid.AllowResizing.None, // disable resizing headersVisibility: wijmo.grid.HeadersVisibility.None, // hide headers selectionMode: wijmo.grid.SelectionMode.ListBox // use ListBox selection });
.custom-flex-grid .wj-header.wj-cell { color: #fff; background-color: #000; border-bottom: solid 1px #404040; border-right: solid 1px #404040; font-weight: bold; } .custom-flex-grid .wj-cell { background-color: #fff; border: none; } .custom-flex-grid .wj-alt:not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: #fff; } .custom-flex-grid .wj-state-selected { background: #000; color: #fff; } .custom-flex-grid .wj-state-multi-selected { background: #222; color: #fff; }

Result (live):

处理null值

默认情况下,FlexGrid允许你在字符串类型的列输入空值,不允许空值或者null在任何其它类型的列中。

你可以使用表格列的required属性来更改此行为。如果你设置required属性为false, 表格会允许你在这一列键入空值,不管这一列是什么数据类型。 相反,如果你设置required属性为true,表格不允许你输入空值,甚至在字符串类型的列中也不允许。

设置required为null恢复默认的行为(只允许在字符串类型列输入空值)。

下面的表格恢复了默认的行为。 它把第一类的下面的表格恢复了默认的行为。它把第一列的required属性设为false,其他列设为true。 你可以通过输入一个空字符串或者只要按下删除键来删除不需要的内容。

<div id="nvGrid"></div>
// create a grid and define the columns new wijmo.grid.FlexGrid('#nvGrid', { autoGenerateColumns: false, itemsSource: data.getData(100), columns: [ { header: 'Country', binding: 'country', width: '*', required: true }, { header: 'Date', binding: 'date', required: false }, { header: 'Revenue', binding: 'amount', format: 'n0', required: false }, { header: 'Active', binding: 'active', required: false } ] });

Result (live):