グリッド:カスタムエディタ

FlexGridはデフォルトで効率的なExcelスタイルの編集を提供しますが、編集の動作をカスタマイズすることができます。この例では、Wijmoコントロールをグリッドエディタとして使用できるようにするCustomGridEditorクラスを定義します。

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as wjGrid from '@grapecity/wijmo.grid'; import * as wjInput from '@grapecity/wijmo.input'; import * as wjCore from '@grapecity/wijmo'; // // *** CustomGridEditor class (transpiled from TypeScript) *** // export class CustomGridEditor { /** * Initializes a new instance of a CustomGridEditor. */ constructor(flex, binding, edtClass, options) { // save references this._grid = flex; this._col = flex.columns.getColumn(binding); // create editor this._ctl = new edtClass(document.createElement('div'), options); // connect grid events flex.beginningEdit.addHandler(this._beginningEdit, this); flex.sortingColumn.addHandler(() => { this._commitRowEdits(); }); flex.scrollPositionChanged.addHandler(() => { if (this._ctl.containsFocus()) { flex.focus(); } }); flex.selectionChanging.addHandler((s, e) => { if (e.row != s.selection.row) { this._commitRowEdits(); } }); // connect editor events this._ctl.addEventListener(this._ctl.hostElement, 'keydown', (e) => { switch (e.keyCode) { case wjCore.Key.Tab: case wjCore.Key.Enter: e.preventDefault(); // TFS 255685 this._closeEditor(true); this._grid.focus(); // forward event to the grid so it will move the selection var evt = document.createEvent('HTMLEvents'); evt.initEvent('keydown', true, true); 'altKey,metaKey,ctrlKey,shiftKey,keyCode'.split(',').forEach((prop) => { evt[prop] = e[prop]; }); this._grid.hostElement.dispatchEvent(evt); break; case wjCore.Key.Escape: this._closeEditor(false); this._grid.focus(); break; } }); // close the editor when it loses focus this._ctl.lostFocus.addHandler(() => { setTimeout(() => { if (!this._ctl.containsFocus()) { this._closeEditor(true); // apply edits and close editor this._grid.onLostFocus(); // commit item edits if the grid lost focus } }); }); // commit edits when grid loses focus this._grid.lostFocus.addHandler(() => { setTimeout(() => { if (!this._grid.containsFocus() && !CustomGridEditor._isEditing) { this._commitRowEdits(); } }); }); // open drop-down on f4/alt-down this._grid.addEventListener(this._grid.hostElement, 'keydown', (e) => { // open drop-down on f4/alt-down this._openDropDown = false; if (e.keyCode == wjCore.Key.F4 || (e.altKey && (e.keyCode == wjCore.Key.Down || e.keyCode == wjCore.Key.Up))) { var colIndex = this._grid.selection.col; if (colIndex > -1 && this._grid.columns[colIndex] == this._col) { this._openDropDown = true; this._grid.startEditing(true); e.preventDefault(); } } // commit edits on Enter (in case we're at the last row, TFS 268944) if (e.keyCode == wjCore.Key.Enter) { this._commitRowEdits(); } }, true); // close editor when user resizes the window // REVIEW: hides editor when soft keyboard pops up (TFS 326875) window.addEventListener('resize', () => { if (this._ctl.containsFocus()) { this._closeEditor(true); this._grid.focus(); } }); } // gets an instance of the control being hosted by this grid editor get control() { return this._ctl; } // handle the grid's beginningEdit event by canceling the built-in editor, // initializing the custom editor and giving it the focus. _beginningEdit(grid, args) { // check that this is our column if (grid.columns[args.col] != this._col) { return; } // check that this is not the Delete key // (which is used to clear cells and should not be messed with) var evt = args.data; if (evt && evt.keyCode == wjCore.Key.Delete) { return; } // cancel built-in editor args.cancel = true; // save cell being edited this._rng = args.range; CustomGridEditor._isEditing = true; // initialize editor host var rcCell = grid.getCellBoundingRect(args.row, args.col), rcBody = document.body.getBoundingClientRect(), ptOffset = new wjCore.Point(-rcBody.left, -rcBody.top), zIndex = (args.row < grid.frozenRows || args.col < grid.frozenColumns) ? '3' : ''; wjCore.setCss(this._ctl.hostElement, { position: 'absolute', left: rcCell.left - 1 + ptOffset.x, top: rcCell.top - 1 + ptOffset.y, width: rcCell.width + 1, height: grid.rows[args.row].renderHeight + 1, borderRadius: '0px', zIndex: zIndex, }); // initialize editor content if (!wjCore.isUndefined(this._ctl['text'])) { this._ctl['text'] = grid.getCellData(this._rng.row, this._rng.col, true); } else { throw 'Can\'t set editor value/text...'; } // start editing item var ecv = grid.editableCollectionView, item = grid.rows[args.row].dataItem; if (ecv && item && item != ecv.currentEditItem) { setTimeout(function () { grid.onRowEditStarting(args); ecv.editItem(item); grid.onRowEditStarted(args); }, 50); // wait for the grid to commit edits after losing focus } // activate editor document.body.appendChild(this._ctl.hostElement); this._ctl.focus(); setTimeout(() => { // get the key that triggered the editor var key = (evt && evt.charCode > 32) ? String.fromCharCode(evt.charCode) : null; // get input element in the control var input = this._ctl.hostElement.querySelector('input'); // send key to editor if (input) { if (key) { input.value = key; wjCore.setSelectionRange(input, key.length, key.length); var evtInput = document.createEvent('HTMLEvents'); evtInput.initEvent('input', true, false); input.dispatchEvent(evtInput); } else { input.select(); } } // give the control focus if (!input && !this._openDropDown) { this._ctl.focus(); } // open drop-down on F4/alt-down if (this._openDropDown && this._ctl instanceof wjInput.DropDown) { this._ctl.isDroppedDown = true; this._ctl.dropDown.focus(); } }, 50); } // close the custom editor, optionally saving the edits back to the grid _closeEditor(saveEdits) { if (this._rng) { var flexGrid = this._grid, ctl = this._ctl, host = ctl.hostElement; // raise grid's cellEditEnding event var e = new wjGrid.CellEditEndingEventArgs(flexGrid.cells, this._rng); flexGrid.onCellEditEnding(e); // save editor value into grid if (saveEdits) { if (!wjCore.isUndefined(ctl['value'])) { this._grid.setCellData(this._rng.row, this._rng.col, ctl['value']); } else if (!wjCore.isUndefined(ctl['text'])) { this._grid.setCellData(this._rng.row, this._rng.col, ctl['text']); } else { throw 'Can\'t get editor value/text...'; } this._grid.invalidate(); } // close editor and remove it from the DOM if (ctl instanceof wjInput.DropDown) { ctl.isDroppedDown = false; } host.parentElement.removeChild(host); this._rng = null; CustomGridEditor._isEditing = false; // raise grid's cellEditEnded event flexGrid.onCellEditEnded(e); } } // commit row edits, fire row edit end events (TFS 339615) _commitRowEdits() { var flexGrid = this._grid, ecv = flexGrid.editableCollectionView; this._closeEditor(true); if (ecv && ecv.currentEditItem) { var e = new wjGrid.CellEditEndingEventArgs(flexGrid.cells, flexGrid.selection); ecv.commitEdit(); setTimeout(() => { flexGrid.onRowEditEnding(e); flexGrid.onRowEditEnded(e); flexGrid.invalidate(); }); } } } // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // // create some random data var countries = 'アメリカ,ドイツ,イギリス,日本,イタリア,ギリシャ'.split(','); var products = [ { id: 0, name: 'ウィジェット', unitPrice: 23.43 }, { id: 1, name: 'ガジェット', unitPrice: 12.33 }, { id: 2, name: 'ツール', unitPrice: 53.07 } ]; var data = []; var dt = new Date(); for (var i = 0; i < 100; i++) { data.push({ id: i, date: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60), time: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60), country: countries[Math.floor(Math.random() * countries.length)], product: products[Math.floor(Math.random() * products.length)].name, amount: Math.random() * 10000 - 5000, discount: Math.random() / 4 }); } // // grid with custom editors var theGrid = new wjGrid.FlexGrid('#theGrid', { keyActionTab: 'CycleOut', autoGenerateColumns: false, itemsSource: data, columns: [ { header: 'ID', binding: 'id', width: 40, isReadOnly: true }, { header: '日付', binding: 'date', format: 'd' }, { header: '時刻', binding: 'time', format: 't' }, { header: '国', binding: 'country' }, { header: '商品', binding: 'product' }, { header: '金額', binding: 'amount', format: 'n2' } ], }); // // add custom editors to the grid new CustomGridEditor(theGrid, 'date', wjInput.InputDate, { format: 'd' }); new CustomGridEditor(theGrid, 'time', wjInput.InputTime, { format: 't', min: new Date(2000, 1, 1, 7, 0), max: new Date(2000, 1, 1, 22, 0), step: 30 }); new CustomGridEditor(theGrid, 'country', wjInput.ComboBox, { itemsSource: countries }); new CustomGridEditor(theGrid, 'amount', wjInput.InputNumber, { format: 'n2', step: 10 }); // // create an editor based on a ComboBox var multiColumnEditor = new CustomGridEditor(theGrid, 'product', wjInput.ComboBox, { headerPath: 'name', displayMemberPath: 'name', itemsSource: products }); // // customize the ComboBox to show multiple columns var combo = multiColumnEditor.control; combo.listBox.formatItem.addHandler(function (s, e) { e.item.innerHTML = '<table><tr>' + '<td style="width:30px;text-align:right;padding-right:6px">' + e.data.id + '</td>' + '<td style="width:100px;padding-right:6px"><b>' + e.data.name + '</b></td>' + '<td style="width:80px;text-align:right;padding-right:6px">' + wjCore.Globalize.format(e.data.unitPrice, 'c') + '</td>' + '</tr></table>'; }); } //# sourceMappingURL=CustomGridEditor.js.map
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Custom Editors</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div id="theGrid"> </div> </div> </body> </html>
.wj-flexgrid { height: 300px; margin-bottom: 12px; } .wj-flexgrid .wj-cell { padding: 6px 3px; } body { margin-bottom: 24px; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@grapecity/wijmo': 'npm:@grapecity/wijmo/index.js', '@grapecity/wijmo.input': 'npm:@grapecity/wijmo.input/index.js', '@grapecity/wijmo.styles': 'npm:@grapecity/wijmo.styles', '@grapecity/wijmo.cultures': 'npm:@grapecity/wijmo.cultures', '@grapecity/wijmo.chart': 'npm:@grapecity/wijmo.chart/index.js', '@grapecity/wijmo.chart.analytics': 'npm:@grapecity/wijmo.chart.analytics/index.js', '@grapecity/wijmo.chart.animation': 'npm:@grapecity/wijmo.chart.animation/index.js', '@grapecity/wijmo.chart.annotation': 'npm:@grapecity/wijmo.chart.annotation/index.js', '@grapecity/wijmo.chart.finance': 'npm:@grapecity/wijmo.chart.finance/index.js', '@grapecity/wijmo.chart.finance.analytics': 'npm:@grapecity/wijmo.chart.finance.analytics/index.js', '@grapecity/wijmo.chart.hierarchical': 'npm:@grapecity/wijmo.chart.hierarchical/index.js', '@grapecity/wijmo.chart.interaction': 'npm:@grapecity/wijmo.chart.interaction/index.js', '@grapecity/wijmo.chart.radar': 'npm:@grapecity/wijmo.chart.radar/index.js', '@grapecity/wijmo.chart.render': 'npm:@grapecity/wijmo.chart.render/index.js', '@grapecity/wijmo.chart.webgl': 'npm:@grapecity/wijmo.chart.webgl/index.js', '@grapecity/wijmo.gauge': 'npm:@grapecity/wijmo.gauge/index.js', '@grapecity/wijmo.grid': 'npm:@grapecity/wijmo.grid/index.js', '@grapecity/wijmo.grid.detail': 'npm:@grapecity/wijmo.grid.detail/index.js', '@grapecity/wijmo.grid.filter': 'npm:@grapecity/wijmo.grid.filter/index.js', '@grapecity/wijmo.grid.search': 'npm:@grapecity/wijmo.grid.search/index.js', '@grapecity/wijmo.grid.grouppanel': 'npm:@grapecity/wijmo.grid.grouppanel/index.js', '@grapecity/wijmo.grid.multirow': 'npm:@grapecity/wijmo.grid.multirow/index.js', '@grapecity/wijmo.grid.transposed': 'npm:@grapecity/wijmo.grid.transposed/index.js', '@grapecity/wijmo.grid.pdf': 'npm:@grapecity/wijmo.grid.pdf/index.js', '@grapecity/wijmo.grid.sheet': 'npm:@grapecity/wijmo.grid.sheet/index.js', '@grapecity/wijmo.grid.xlsx': 'npm:@grapecity/wijmo.grid.xlsx/index.js', '@grapecity/wijmo.grid.selector': 'npm:@grapecity/wijmo.grid.selector/index.js', '@grapecity/wijmo.grid.cellmaker': 'npm:@grapecity/wijmo.grid.cellmaker/index.js', '@grapecity/wijmo.nav': 'npm:@grapecity/wijmo.nav/index.js', '@grapecity/wijmo.odata': 'npm:@grapecity/wijmo.odata/index.js', '@grapecity/wijmo.olap': 'npm:@grapecity/wijmo.olap/index.js', '@grapecity/wijmo.pdf': 'npm:@grapecity/wijmo.pdf/index.js', '@grapecity/wijmo.viewer': 'npm:@grapecity/wijmo.viewer/index.js', '@grapecity/wijmo.xlsx': 'npm:@grapecity/wijmo.xlsx/index.js', '@grapecity/wijmo.undo': 'npm:@grapecity/wijmo.undo/index.js', '@grapecity/wijmo.interop.grid': 'npm:@grapecity/wijmo.interop.grid/index.js', '@grapecity/wijmo.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);