示例
行内编辑
尽管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 = 'US,Germany,UK,Japan,Sweden,Norway,Denmark'.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: 'Country', isRequired: true, dataMap: countries },
{ binding: 'sales', header: 'Sales', format: 'n2' },
{ binding: 'expenses', header: 'Expenses', format: 'n2' },
{ binding: 'buttons', header: 'Edit', 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> Edit
</button>
<button id="btnDelete" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-remove"></span> Delete
</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> Cancel
</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 = ['US', 'Germany', 'UK' ,'Japan', 'Italy', 'Greece'];
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]="'Country'" [isRequired]="true" [dataMap]="countries"></wj-flex-grid-column>
<wj-flex-grid-column [binding]="'sales'" [header]="'Sales'" [format]="'n2'"></wj-flex-grid-column>
<wj-flex-grid-column [binding]="'expenses'" [header]="'Expenses'" [format]="'n2'"></wj-flex-grid-column>
<wj-flex-grid-column [binding]="'buttons'" [header]="'Edit'" [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> Edit
</button>
<button id="btnDelete" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-remove"></span> Delete
</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> Cancel
</button>
</div>
</div>
.wj-flexgrid {
max-height: 200px;
margin-bottom: 12px;
}
body {
margin-bottom: 24px;
}
<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="Country" :isRequired=true :dataMap="countries"></wj-flex-grid-column>
<wj-flex-grid-column binding="sales" header="Sales" format="n2"></wj-flex-grid-column>
<wj-flex-grid-column binding="expenses" header="Expenses" format="n2"></wj-flex-grid-column>
<wj-flex-grid-column binding="buttons" header="Edit" :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> Edit
</button>
<button id="btnDelete" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-remove"></span> Delete
</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> Cancel
</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 : ['US', 'Germany', 'UK' ,'Japan', 'Italy', 'Greece']
},
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>
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 = ['US', 'Germany', 'UK', 'Japan', 'Italy', 'Greece'];
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="Country" isRequired={true}></wjGrid.FlexGridColumn>
<wjGrid.FlexGridColumn binding="sales" header="Sales" format="n2"></wjGrid.FlexGridColumn>
<wjGrid.FlexGridColumn binding="expenses" header="Expenses" format="n2"></wjGrid.FlexGridColumn>
<wjGrid.FlexGridColumn binding="buttons" header="Edit" width={160}></wjGrid.FlexGridColumn>
</wjGrid.FlexGrid>
<div id="tplBtnViewMode">
<button id="btnEdit" className="btn btn-default btn-sm">
<span className="glyphicon glyphicon-pencil"></span> Edit
</button>
<button id="btnDelete" className="btn btn-default btn-sm">
<span className="glyphicon glyphicon-remove"></span> Delete
</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> Cancel
</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;
}