グリッド:インライン編集

FlexGridは効率的なExcelスタイルの編集をデフォルトで提供しますが、編集の動作をカスタマイズすることができます。Excelスタイルの編集よりも、典型的な編集可能なHTMLテーブルのようにすべての行に編集ボタンを追加することを好む場合は、formatItemイベントと少しのコードを使用することで実現することができます。

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as wjGrid from '@grapecity/wijmo.grid'; import * as wjCore from '@grapecity/wijmo'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // // create some random data var countries = 'アメリカ,ドイツ,イギリス,日本,スウェーデン,ノルウェー,デンマーク'.split(','); var data = []; for (var i = 0; i < countries.length; i++) { data.push({ id: i, country: countries[i], sales: Math.random() * 10000, expenses: Math.random() * 5000, overdue: (i + 1) % 4 == 0 }); } // // create the grid with custom editing behavior var theGrid = new wjGrid.FlexGrid('#theGrid', { isReadOnly: true, selectionMode: 'None', headersVisibility: 'Column', itemsSource: data, columns: [ { binding: 'id', header: 'ID', width: 50 }, { binding: 'country', header: '国', isRequired: true, dataMap: countries }, { binding: 'sales', header: '売上', format: 'n2' }, { binding: 'expenses', header: '費用', format: 'n2' }, { binding: 'buttons', header: '編集', width: 160 } ] }); // // make rows taller to accommodate edit buttons and input controls theGrid.rows.defaultSize = 40; // // custom formatter to paint buttons and editors theGrid.formatItem.addHandler(function (s, e) { if (e.panel == s.cells) { var col = s.columns[e.col], item = s.rows[e.row].dataItem; // if (item == currentEditItem) { // // create editors and buttons for the item being edited switch (col.binding) { case 'buttons': e.cell.innerHTML = document.getElementById('tplBtnEditMode').innerHTML; e.cell['dataItem'] = item; break; case 'country': case 'sales': case 'expenses': e.cell.innerHTML = '<input class="form-control" ' + 'id="' + col.binding + '" ' + 'value="' + s.getCellData(e.row, e.col, true) + '"/>'; break; } } else { // // create buttons for items not being edited switch (col.binding) { case 'buttons': e.cell.innerHTML = document.getElementById('tplBtnViewMode').innerHTML; e.cell['dataItem'] = item; break; } } } }); // // handle button clicks theGrid.addEventListener(theGrid.hostElement, 'click', function (e) { let targetBtn; if (e.target instanceof HTMLButtonElement) { targetBtn = e.target; } else if (e.target instanceof HTMLSpanElement && e.target.classList.contains('glyphicon')) { targetBtn = e.target.parentElement; } if (targetBtn) { // // get button's data item var item = wjCore.closest(targetBtn, '.wj-cell')['dataItem']; // // handle buttons switch (targetBtn.id) { // // start editing this item case 'btnEdit': editItem(item); break; // // remove this item from the collection case 'btnDelete': theGrid.collectionView.remove(item); break; // // commit edits case 'btnOK': commitEdit(); break; // // cancel edits case 'btnCancel': cancelEdit(); break; } } }); // // exit edit mode when scrolling the grid or losing focus theGrid.scrollPositionChanged.addHandler(cancelEdit); theGrid.lostFocus.addHandler(cancelEdit); // // editing commands var currentEditItem = null; // function editItem(item) { cancelEdit(); currentEditItem = item; theGrid.invalidate(); } // function commitEdit() { if (currentEditItem) { theGrid.columns.forEach(function (col) { var input = theGrid.hostElement.querySelector('#' + col.binding); if (input) { var value = wjCore.changeType(input.value, col.dataType, col.format); if (wjCore.getType(value) == col.dataType) { currentEditItem[col.binding] = value; } } }); } currentEditItem = null; theGrid.invalidate(); } // function cancelEdit() { if (currentEditItem) { currentEditItem = null; theGrid.invalidate(); } } } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Inline Editing</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> <!-- template for buttons on items in view mode --> <div id="tplBtnViewMode" style="display:none"> <button id="btnEdit" class="btn btn-default btn-sm"> <span class="glyphicon glyphicon-pencil"></span> 編集 </button> <button id="btnDelete" class="btn btn-default btn-sm"> <span class="glyphicon glyphicon-remove"></span> 削除 </button> <div> <!-- template for buttons on items in edit mode --> <div id="tplBtnEditMode" style="display:none"> <button id="btnOK" class="btn btn-primary btn-sm"> <span class="glyphicon glyphicon-ok"></span> OK </button> <button id="btnCancel" class="btn btn-warning btn-sm"> <span class="glyphicon glyphicon-ban-circle"></span> キャンセル </button> </div> </div> </body> </html> .wj-flexgrid { max-height: 200px; margin-bottom: 12px; } body { margin-bottom: 24px; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import { Component, enableProdMode, NgModule, ViewChild } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { WjGridModule } from '@grapecity/wijmo.angular2.grid'; import * as wjcCore from '@grapecity/wijmo'; import * as wjcGrid from '@grapecity/wijmo.grid'; import '@grapecity/wijmo.input'; @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { data: any[]; countries = ['アメリカ', 'ドイツ', 'イギリス' ,'日本', 'イタリア', 'ギリシャ']; private _currentEditItem: any = null; constructor() { this.data = this._getData(); } @ViewChild('flex') flex: wjcGrid.FlexGrid; initializeGrid(flex: wjcGrid.FlexGrid) { flex.rows.defaultSize = 40; // custom formatter to paint buttons and editors flex.formatItem.addHandler((s: wjcGrid.FlexGrid, e: wjcGrid.FormatItemEventArgs) => { if (e.panel == s.cells) { let col = s.columns[e.col], item = s.rows[e.row].dataItem; if (item == this._currentEditItem) { // create editors and buttons for the item being edited switch (col.binding) { case 'buttons': e.cell.innerHTML = document.getElementById('tplBtnEditMode').innerHTML; e.cell['dataItem'] = item; break; case 'country': case 'sales': case 'expenses': e.cell.innerHTML = '<input class="form-control" ' + 'id="' + col.binding + '" ' + 'value="' + s.getCellData(e.row, e.col, true) + '"/>'; break; } } else { // create buttons for items not being edited switch (col.binding) { case 'buttons': e.cell.innerHTML = document.getElementById('tplBtnViewMode').innerHTML; e.cell['dataItem'] = item; break; } } } }); // handle button clicks flex.addEventListener(flex.hostElement, 'click', (e: MouseEvent) => { let targetBtn: HTMLButtonElement; if (e.target instanceof HTMLButtonElement) { targetBtn = e.target; } else if (e.target instanceof HTMLSpanElement && e.target.classList.contains('glyphicon')) { targetBtn = e.target.parentElement as HTMLButtonElement; } if (targetBtn) { // get button's data item let item = wjcCore.closest(targetBtn, '.wj-cell')['dataItem']; // handle buttons switch (targetBtn.id) { // start editing this item case 'btnEdit': this._editItem(item); break; // remove this item from the collection case 'btnDelete': (<wjcCore.CollectionView>flex.collectionView).remove(item); break; // commit edits case 'btnOK': this._commitEdit(); break; // cancel edits case 'btnCancel': this._cancelEdit(); break; } } }); // exit edit mode when scrolling the grid or losing focus flex.scrollPositionChanged.addHandler(this._cancelEdit.bind(this)); flex.lostFocus.addHandler(this._cancelEdit.bind(this)); } private _getData() { // create some random data let data = []; for (let i = 0; i < this.countries.length; i++) { data.push({ id: i, country: this.countries[i], sales: Math.random() * 10000, expenses: Math.random() * 5000, overdue: i % 4 == 0 }); } return data; } private _editItem(item: any) { this._cancelEdit(); this._currentEditItem = item; this.flex.invalidate(); } private _commitEdit() { if (this._currentEditItem) { this.flex.columns.forEach((col: wjcGrid.Column) => { let input = <HTMLInputElement>this.flex.hostElement.querySelector('#' + col.binding); if (input) { let value = wjcCore.changeType(input.value, col.dataType, col.format); if (wjcCore.getType(value) == col.dataType) { this._currentEditItem[col.binding] = value; } } }); } this._currentEditItem = null; this.flex.invalidate(); } private _cancelEdit() { if (this._currentEditItem) { this._currentEditItem = null; this.flex.invalidate(); } } } @NgModule({ imports: [WjGridModule, BrowserModule], declarations: [AppComponent], bootstrap: [AppComponent] }) export class AppModule { } enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Inline Editing</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html> <div class="container-fluid"> <!-- the grid --> <wj-flex-grid #flex [isReadOnly]="true" [selectionMode]="'None'" [headersVisibility]="'Column'" [(itemsSource)]="data" (initialized)="initializeGrid(flex)"> <wj-flex-grid-column [binding]="'id'" [header]="'ID'" [width]="50" [isReadOnly]="true"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'country'" [header]="'国'" [isRequired]="true" [dataMap]="countries"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'sales'" [header]="'売上'" [format]="'n2'"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'expenses'" [header]="'費用'" [format]="'n2'"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'buttons'" [header]="'編集'" [width]="160"> </wj-flex-grid-column> </wj-flex-grid> <!-- template for buttons on items in view mode --> <div id="tplBtnViewMode" style="display:none"> <button id="btnEdit" class="btn btn-default btn-sm"> <span class="glyphicon glyphicon-pencil"></span> 編集 </button> <button id="btnDelete" class="btn btn-default btn-sm"> <span class="glyphicon glyphicon-remove"></span> 削除 </button> <div> <!-- template for buttons on items in edit mode --> <div id="tplBtnEditMode" style="display:none"> <button id="btnOK" class="btn btn-primary btn-sm"> <span class="glyphicon glyphicon-ok"></span> OK </button> <button id="btnCancel" class="btn btn-warning btn-sm"> <span class="glyphicon glyphicon-ban-circle"></span> キャンセル </button> </div> </div> .wj-flexgrid { max-height: 200px; margin-bottom: 12px; } body { margin-bottom: 24px; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; import * as wjGrid from '@grapecity/wijmo.react.grid'; import * as wjcCore from '@grapecity/wijmo'; class App extends React.Component { constructor(props) { super(props); this.countries = ['アメリカ', 'ドイツ', 'イギリス', '日本', 'イタリア', 'ギリシャ']; this.state = { data: this.getData() }; } render() { return <div className="container-fluid"> <wjGrid.FlexGrid isReadOnly={true} selectionMode="None" headersVisibility="Column" initialized={this.initializeGrid.bind(this)} itemsSource={this.state.data}> <wjGrid.FlexGridColumn binding="id" header="ID" width={50}></wjGrid.FlexGridColumn> <wjGrid.FlexGridColumn binding="country" header="国" isRequired={true}></wjGrid.FlexGridColumn> <wjGrid.FlexGridColumn binding="sales" header="売上" format="n2"></wjGrid.FlexGridColumn> <wjGrid.FlexGridColumn binding="expenses" header="費用" format="n2"></wjGrid.FlexGridColumn> <wjGrid.FlexGridColumn binding="buttons" header="編集" width={160}></wjGrid.FlexGridColumn> </wjGrid.FlexGrid> <div id="tplBtnViewMode"> <button id="btnEdit" className="btn btn-default btn-sm"> <span className="glyphicon glyphicon-pencil"></span> 編集 </button> <button id="btnDelete" className="btn btn-default btn-sm"> <span className="glyphicon glyphicon-remove"></span> 削除 </button> </div> <div id="tplBtnEditMode"> <button id="btnOK" className="btn btn-primary btn-sm"> <span className="glyphicon glyphicon-ok"></span> OK </button> <button id="btnCancel" className="btn btn-warning btn-sm"> <span className="glyphicon glyphicon-ban-circle"></span> キャンセル </button> </div> </div>; } initializeGrid(flex) { this.flex = flex; flex.rows.defaultSize = 40; // custom formatter to paint buttons and editors flex.formatItem.addHandler((s, e) => { if (e.panel == s.cells) { let col = s.columns[e.col], item = s.rows[e.row].dataItem; if (item == this._currentEditItem) { // create editors and buttons for the item being edited switch (col.binding) { case 'buttons': e.cell.innerHTML = document.getElementById('tplBtnEditMode').innerHTML; e.cell['dataItem'] = item; break; case 'country': case 'sales': case 'expenses': e.cell.innerHTML = '<input class="form-control" ' + 'id="' + col.binding + '" ' + 'value="' + s.getCellData(e.row, e.col, true) + '"/>'; break; } } else { // create buttons for items not being edited switch (col.binding) { case 'buttons': e.cell.innerHTML = document.getElementById('tplBtnViewMode').innerHTML; e.cell['dataItem'] = item; break; } } } }); // handle button clicks flex.addEventListener(flex.hostElement, 'click', (e) => { if (e.target instanceof HTMLButtonElement) { // get button's data item let item = wjcCore.closest(e.target, '.wj-cell')['dataItem']; // handle buttons switch (e.target.id) { // start editing this item case 'btnEdit': this._editItem(item); break; // remove this item from the collection case 'btnDelete': (flex.collectionView).remove(item); break; // commit edits case 'btnOK': this._commitEdit(); break; // cancel edits case 'btnCancel': this._cancelEdit(); break; } } }); // exit edit mode when scrolling the grid or losing focus flex.scrollPositionChanged.addHandler(this._cancelEdit.bind(this)); flex.lostFocus.addHandler(this._cancelEdit.bind(this)); } getData() { // create some random data let data = []; for (let i = 0; i < this.countries.length; i++) { data.push({ id: i, country: this.countries[i], sales: Math.random() * 10000, expenses: Math.random() * 5000, overdue: i % 4 == 0 }); } return data; } _editItem(item) { this._cancelEdit(); this._currentEditItem = item; this.flex.invalidate(); } _commitEdit() { if (this._currentEditItem) { this.flex.columns.forEach((col) => { let input = this.flex.hostElement.querySelector('#' + col.binding); if (input) { let value = wjcCore.changeType(input.value, col.dataType, col.format); if (wjcCore.getType(value) == col.dataType) { this._currentEditItem[col.binding] = value; } } }); } this._currentEditItem = null; this.flex.invalidate(); } _cancelEdit() { if (this._currentEditItem) { this._currentEditItem = null; this.flex.invalidate(); } } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Inline Editing</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 id="app"></div> </body> </html> .wj-flexgrid { max-height: 200px; margin-bottom: 12px; } body { margin-bottom: 24px; } #tplBtnViewMode { display:none; } #tplBtnEditMode { display: none; } <template> <div class="container-fluid"> <!-- the grid --> <wj-flex-grid :isReadOnly=true selectionMode="None" headersVisibility="Column" :itemsSource="data" :initialized="initializeGrid"> <wj-flex-grid-column binding="id" header="ID" :width=50 :isReadOnly=true></wj-flex-grid-column> <wj-flex-grid-column binding="country" header="国" :isRequired=true :dataMap="countries"></wj-flex-grid-column> <wj-flex-grid-column binding="sales" header="売上" format="n2"></wj-flex-grid-column> <wj-flex-grid-column binding="expenses" header="費用" format="n2"></wj-flex-grid-column> <wj-flex-grid-column binding="buttons" header="編集" :width=160> </wj-flex-grid-column> </wj-flex-grid> <!-- template for buttons on items in view mode --> <div id="tplBtnViewMode" style="display:none"> <button id="btnEdit" class="btn btn-default btn-sm"> <span class="glyphicon glyphicon-pencil"></span> 編集 </button> <button id="btnDelete" class="btn btn-default btn-sm"> <span class="glyphicon glyphicon-remove"></span> 削除 </button> <div> <!-- template for buttons on items in edit mode --> <div id="tplBtnEditMode" style="display:none"> <button id="btnOK" class="btn btn-primary btn-sm"> <span class="glyphicon glyphicon-ok"></span> OK </button> <button id="btnCancel" class="btn btn-warning btn-sm"> <span class="glyphicon glyphicon-ban-circle"></span> キャンセル </button> </div> </div> </template> <script> import "@grapecity/wijmo.styles/wijmo.css"; import "bootstrap.css"; import Vue from "vue"; import "@grapecity/wijmo.vue2.grid"; import * as wjcCore from '@grapecity/wijmo'; import * as wjcGrid from '@grapecity/wijmo.grid'; import '@grapecity/wijmo.input'; new Vue({ el: "#app", data: { data: [], countries : ['アメリカ', 'ドイツ', 'イギリス' ,'日本', 'イタリア', 'ギリシャ'] }, methods:{ initializeGrid(flex) { this.flex = flex; this.data = this._getData(); flex.rows.defaultSize = 40; // custom formatter to paint buttons and editors flex.formatItem.addHandler((s, e) => { if (e.panel == s.cells) { let col = s.columns[e.col], item = s.rows[e.row].dataItem; if (item == this._currentEditItem) { // create editors and buttons for the item being edited switch (col.binding) { case 'buttons': e.cell.innerHTML = document.getElementById('tplBtnEditMode').innerHTML; e.cell['dataItem'] = item; break; case 'country': case 'sales': case 'expenses': e.cell.innerHTML = '<input class="form-control" ' + 'id="' + col.binding + '" ' + 'value="' + s.getCellData(e.row, e.col, true) + '"/>'; break; } } else { // create buttons for items not being edited switch (col.binding) { case 'buttons': e.cell.innerHTML = document.getElementById('tplBtnViewMode').innerHTML; e.cell['dataItem'] = item; break; } } } }); // handle button clicks flex.addEventListener(flex.hostElement, 'click', (e) => { let targetBtn; if (e.target instanceof HTMLButtonElement) { targetBtn = e.target; } else if (e.target instanceof HTMLSpanElement && e.target.classList.contains('glyphicon')) { targetBtn = e.target.parentElement; } if (targetBtn) { // get button's data item let item = wjcCore.closest(targetBtn, '.wj-cell')['dataItem']; // handle buttons switch (targetBtn.id) { // start editing this item case 'btnEdit': this._editItem(item); break; // remove this item from the collection case 'btnDelete': (flex.collectionView).remove(item); break; // commit edits case 'btnOK': this._commitEdit(); break; // cancel edits case 'btnCancel': this._cancelEdit(); break; } } }); // exit edit mode when scrolling the grid or losing focus flex.scrollPositionChanged.addHandler(this._cancelEdit.bind(this)); flex.lostFocus.addHandler(this._cancelEdit.bind(this)); }, _getData() { // create some random data let data = []; for (let i = 0; i < this.countries.length; i++) { data.push({ id: i, country: this.countries[i], sales: Math.random() * 10000, expenses: Math.random() * 5000, overdue: i % 4 == 0 }); } return data; }, _editItem(item) { this._cancelEdit(); this._currentEditItem = item; this.flex.invalidate(); }, _commitEdit() { if (this._currentEditItem) { this.flex.columns.forEach((col) => { let input = this.flex.hostElement.querySelector('#' + col.binding); if (input) { let value = wjcCore.changeType(input.value, col.dataType, col.format); if (wjcCore.getType(value) == col.dataType) { this._currentEditItem[col.binding] = value; } } }); } this._currentEditItem = null; this.flex.invalidate(); }, _cancelEdit() { if (this._currentEditItem) { this._currentEditItem = null; this.flex.invalidate(); } } } }); </script> <style> .wj-flexgrid { max-height: 200px; margin-bottom: 12px; } body { margin-bottom: 48px; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Inline Editing</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.vue'); </script> </head> <body> <div id="app"> </div> </body> </html>