デフォルトでは、TableSheet はローカル データ マネージャーと対話するだけです。変更されたデータをリモート データベースと同期する場合は、最初にAutoSyncまたはBatchモードを有効にします。
このデモはBatchモードを使用します。
AutoSyncモード
このモードは主に、使用頻度の低いデータ操作のシナリオに適しています。行アクションのボタンやAPIを使って行を操作すると、対応する変更を伴うリクエストが開始され、直ちにサーバーに送信されます。
テーブルの初期化でAutoSyncモードを有効にするには:
Batchモード
このモードは主に、データを頻繁に操作するシナリオに適しています。これは、各行の操作を順番に保存し、すべての変更をコレクションにパッケージして、ネットワークリソースを節約するために一度にサーバに送信します。
テーブルの初期化でBatchモードを有効にして、BatchApiUrlを指定するには:
その後、すべての変更内容を送信または破棄できます:
変更の取得
サーバーに保存する前に変更を取得することもできます。
要求と応答
操作
要求の種類
要求データ
応答データ
更新
POST
更新されたデータ
更新されたデータ
読み込み
GET
データなし
レコード配列
削除
DELETE
削除されたデータまたはデータ配列
制限なし
作成
POST
挿入されたデータ
挿入されたデータ
一括更新
POST
各オブジェクトに'type'プロパティが含まれるオブジェクトの配列。
この操作タイプは'update'、'insert'または'delete'のいずれかです。
'dataItem'プロパティは、現在のレコードを表します。
'sourceIndex'プロパティは、レコードのインデックスです。
オプションの'oldDataItem'プロパティは、元のレコードです。
例えば:
[ {"type":"delete","dataItem":{...}, "sourceIndex":5},
{"type":"insert","dataItem":{...}, "sourceIndex":3},
{"type":"update","dataItem":{...}, "oldDataItem":{...}, "oldDataItem":{...}, "sourceIndex":1}]
各オブジェクトに、操作の成功または失敗を表す'succeed'プロパティと、現在のレコードで'insert'操作にのみ使用する、オプションの'data'プロパティを含むオブジェクトの配列。
例えば:
[{"succeed":true}, {"succeed":false}, {"succeed": true}]
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import './styles.css';
import { AppFunc } from './app-func';
import { App } from './app-class';
// 1. Functional Component sample
ReactDOM.render(<AppFunc />, document.getElementById('app'));
// 2. Class Component sample
// ReactDOM.render(<App />, document.getElementById('app'));
/*REPLACE_MARKER*/
/*DO NOT DELETE THESE COMMENTS*/
import * as React from 'react';
import { useState } from 'react';
import GC from '@mescius/spread-sheets';
import '@mescius/spread-sheets-resources-ja';
GC.Spread.Common.CultureManager.culture("ja-jp");
import "@mescius/spread-sheets-tablesheet";
import { SpreadSheets } from '@mescius/spread-sheets-react';
import './styles.css';
var tableName = "Employee";
var baseApiUrl = getBaseApiUrl();
var apiUrl = baseApiUrl + "/" + tableName;
var batchApiUrl = baseApiUrl + "/" + tableName + 'Collection';
var tablesheetName = 'MyTableSheet';
let spread = null;
export function AppFunc() {
const [changesContent, setChangesContent] = useState("");
const [tablesheet, setTablesheet] = useState(null);
const [selections, setSelections] = useState([{row: 0, rowCount: 1, col: 0, colCount: 1}]);
let initSpread = function(workbook) {
spread = workbook;
spread.suspendPaint();
spread.clearSheets();
spread.options.autoFitType = GC.Spread.Sheets.AutoFitType.cellWithHeader;
//init a data manager
var dataManager = spread.dataManager();
var myTable = dataManager.addTable("myTable", {
remote: {
read: {
url: apiUrl
},
update: {
url: apiUrl,
method: 'PUT'
},
create: {
url: apiUrl
},
delete: {
url: apiUrl
},
batch: {
url: batchApiUrl
}
},
batch: true,
schema: {
columns: {
"Id": { dataType: "number" },
"LastName": { dataType: "string" },
"FirstName": { dataType: "string" },
"HomePhone": { dataType: "string" },
"Notes": { dataType: "string" }
}
}
});
//init a table sheet
var sheet = spread.addSheetTab(0, tablesheetName, GC.Spread.Sheets.SheetType.tableSheet);
setTablesheet(sheet);
var rowActions = GC.Spread.Sheets.TableSheet.BuiltInRowActions;
var options = sheet.rowActionOptions();
options.push(
rowActions.removeRow,
rowActions.saveRow,
rowActions.resetRow,
);
sheet.rowActionOptions(options);
//bind a view to the table sheet
myTable.fetch().then(function () {
var view = myTable.addView("myView", [
{ value: "Id", width: 50, caption: "ID" },
{ value: "FirstName", width: 100, caption: "First Name" },
{ value: "LastName", width: 100, caption: "Last Name" },
{ value: "Title", width: 150, },
{ value: "HomePhone", width: 150, caption: "Phone" },
{ value: "Notes", width: 250, caption: "Notes" }
]); //the View has all default columns of the Table
sheet.setDataView(view);
});
spread.bind(GC.Spread.Sheets.Events.SelectionChanged, (e, args) => {
setSelections(args.newSelections);
});
spread.resumePaint();
}
let removeRow = function() {
traverseSelectionsRowsWithOperation((row) => {
tablesheet.removeRow(row);
});
}
let saveRow = function() {
traverseSelectionsRowsWithOperation((row) => {
tablesheet.saveRow(row);
});
}
let resetRow = function() {
traverseSelectionsRowsWithOperation((row) => {
tablesheet.resetRow(row);
});
}
let saveAllRows = function() {
spread.commandManager().SaveAll.execute(spread, { sheetName: tablesheetName });
}
let submitChanges = function() {
tablesheet.submitChanges();
}
let discardChanges = function() {
tablesheet.cancelChanges();
}
let getChanges = function() {
let changes = formatChanges(tablesheet.getChanges())
setChangesContent(changes);
}
let traverseSelectionsRowsWithOperation = function(operation) {
if (selections) {
for (var i = 0; i < selections.length; i++) {
var selection = selections[i];
var row = selection.row;
var rowCount = selection.rowCount;
for (var r = row + rowCount - 1; r >= row; r--) {
operation(r);
}
}
}
}
return (
<div class="sample-tutorial">
<div class="sample-spreadsheets">
<SpreadSheets workbookInitialized={spread => initSpread(spread)}></SpreadSheets>
</div>
<div id="options-container" class="options-container">
<fieldset>
<legend>アクティブな行の操作</legend>
<div class="field-line">
<input id="remove" type="button" value="削除" onClick={() => { removeRow() }} />
</div>
<div class="field-line">
<input id="save" type="button" value="保存" onClick={() => { saveRow() }} />
</div>
<div class="field-line">
<input id="reset" type="button" value="リセット" onClick={() => { resetRow() }} />
</div>
</fieldset>
<fieldset>
<legend>すべての行の保存</legend>
<div class="field-line">
<input id="save-all" type="button" value="すべての保存" onClick={() => { saveAllRows() }} />
</div>
</fieldset>
<fieldset>
<legend>一括操作</legend>
<div class="field-line">
<input type="button" value="実行する" id="submit" onClick={() => { submitChanges() }} />
</div>
<div class="field-line">
<input type="button" value="破棄する" id="discard" onClick={() => { discardChanges() }} />
</div>
</fieldset>
<fieldset>
<legend>変更の取得</legend>
<div class="field-line">
<input type="button" value="変更を取得する" id="getChanges" onClick={() => { getChanges() }}/>
</div>
<div class="field-line">
<textarea id="changesPanel" value={changesContent}></textarea>
</div>
</fieldset>
</div>
</div>
);
}
function getBaseApiUrl() {
return window.location.href.match(/http.+spreadjs\/demos\//)[0] + 'server/api';
}
function formatChanges (changes) {
var json = JSON.stringify(changes, function(k, v) {
if (k === "dataItem" || k === "oldDataItem") {
return {
Id: v.Id,
FirstName: v.FirstName,
LastName: v.LastName,
HomePhone: v.HomePhone,
Title: v.Title
};
}
return v;
}, 2);
return json;
}
/*REPLACE_MARKER*/
/*DO NOT DELETE THESE COMMENTS*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import GC from '@mescius/spread-sheets';
import '@mescius/spread-sheets-resources-ja';
GC.Spread.Common.CultureManager.culture("ja-jp");
import "@mescius/spread-sheets-tablesheet";
import { SpreadSheets, Worksheet } from '@mescius/spread-sheets-react';
import './styles.css';
const Component = React.Component;
var tableName = "Employee";
var baseApiUrl = getBaseApiUrl();
var apiUrl = baseApiUrl + "/" + tableName;
var batchApiUrl = baseApiUrl + "/" + tableName + 'Collection';
var tablesheetName = 'MyTableSheet';
export class App extends Component {
constructor(props) {
super(props);
this.spread = null;
this.tablesheet = null;
this.selections = null;
this.state = {changesContent: ""};
}
render() {
return (
<div class="sample-tutorial">
<div class="sample-spreadsheets">
<SpreadSheets workbookInitialized={spread => this.initSpread(spread)}></SpreadSheets>
</div>
<div id="options-container" class="options-container">
<fieldset>
<legend>アクティブな行の操作</legend>
<div class="field-line">
<input id="remove" type="button" value="削除" onClick={() => { this.removeRow() }} />
</div>
<div class="field-line">
<input id="save" type="button" value="保存" onClick={() => { this.saveRow() }} />
</div>
<div class="field-line">
<input id="reset" type="button" value="リセット" onClick={() => { this.resetRow() }} />
</div>
</fieldset>
<fieldset>
<legend>すべての行の保存</legend>
<div class="field-line">
<input id="save-all" type="button" value="すべての保存" onClick={() => { this.saveAllRows() }} />
</div>
</fieldset>
<fieldset>
<legend>一括操作</legend>
<div class="field-line">
<input type="button" value="実行する" id="submit" onClick={() => { this.submitChanges() }} />
</div>
<div class="field-line">
<input type="button" value="破棄する" id="discard" onClick={() => { this.discardChanges() }} />
</div>
</fieldset>
<fieldset>
<legend>変更の取得</legend>
<div class="field-line">
<input type="button" value="変更を取得する" id="getChanges" onClick={() => { this.getChanges() }}/>
</div>
<div class="field-line">
<textarea id="changesPanel" value={this.state.changesContent}></textarea>
</div>
</fieldset>
</div>
</div>
);
}
initSpread(spread) {
this.spread = spread;
spread.suspendPaint();
spread.clearSheets();
spread.options.autoFitType = GC.Spread.Sheets.AutoFitType.cellWithHeader;
//init a data manager
var dataManager = spread.dataManager();
var myTable = dataManager.addTable("myTable", {
remote: {
read: {
url: apiUrl
},
update: {
url: apiUrl,
method: 'PUT'
},
create: {
url: apiUrl
},
delete: {
url: apiUrl
},
batch: {
url: batchApiUrl
}
},
batch: true,
schema: {
columns: {
"Id": { dataType: "number" },
"LastName": { dataType: "string" },
"FirstName": { dataType: "string" },
"HomePhone": { dataType: "string" },
"Notes": { dataType: "string" }
}
}
});
//init a table sheet
var sheet = spread.addSheetTab(0, tablesheetName, GC.Spread.Sheets.SheetType.tableSheet);
this.tablesheet = sheet;
var rowActions = GC.Spread.Sheets.TableSheet.BuiltInRowActions;
var options = sheet.rowActionOptions();
options.push(
rowActions.removeRow,
rowActions.saveRow,
rowActions.resetRow,
);
sheet.rowActionOptions(options);
//bind a view to the table sheet
myTable.fetch().then(function () {
var view = myTable.addView("myView", [
{ value: "Id", width: 50, caption: "ID" },
{ value: "FirstName", width: 100, caption: "First Name" },
{ value: "LastName", width: 100, caption: "Last Name" },
{ value: "Title", width: 150, },
{ value: "HomePhone", width: 150, caption: "Phone" },
{ value: "Notes", width: 250, caption: "Notes" }
]);
sheet.setDataView(view);
});
spread.bind(GC.Spread.Sheets.Events.SelectionChanged, (e, args) => {
this.selections = args.newSelections;
});
spread.resumePaint();
}
removeRow() {
this.traverseSelectionsRowsWithOperation((row) => {
this.tablesheet.removeRow(row);
});
}
saveRow() {
this.traverseSelectionsRowsWithOperation((row) => {
this.tablesheet.saveRow(row);
});
}
resetRow() {
this.traverseSelectionsRowsWithOperation((row) => {
this.tablesheet.resetRow(row);
});
}
saveAllRows() {
this.spread.commandManager().SaveAll.execute(this.spread, { sheetName: tablesheetName });
}
submitChanges() {
this.tablesheet.submitChanges();
}
discardChanges() {
this.tablesheet.cancelChanges();
}
getChanges() {
let changes = formatChanges(this.tablesheet.getChanges())
this.setState({changesContent: changes});
}
traverseSelectionsRowsWithOperation(operation) {
var selections = this.selections;
if (selections) {
for (var i = 0; i < selections.length; i++) {
var selection = selections[i];
var row = selection.row;
var rowCount = selection.rowCount;
for (var r = row + rowCount - 1; r >= row; r--) {
operation(r);
}
}
}
}
}
function getBaseApiUrl() {
return window.location.href.match(/http.+spreadjs\/demos\//)[0] + 'server/api';
}
function formatChanges (changes) {
var json = JSON.stringify(changes, function(k, v) {
if (k === "dataItem" || k === "oldDataItem") {
return {
Id: v.Id,
FirstName: v.FirstName,
LastName: v.LastName,
HomePhone: v.HomePhone,
Title: v.Title
};
}
return v;
}, 2);
return json;
}
<!doctype html>
<html style="height:100%;font-size:14px;">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/ja/react/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<!-- SystemJS -->
<script src="$DEMOROOT$/ja/react/node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('$DEMOROOT$/ja/lib/react/license.js').then(function () {
System.import('./src/app');
});
</script>
</head>
<body>
<div id="app" style="height: 100%;"></div>
</body>
</html>
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
fieldset {
padding: 6px;
margin: 0;
margin-top: 10px;
}
.sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
.sample-spreadsheets {
width: calc(100% - 280px);
height: 100%;
overflow: hidden;
float: left;
}
.options-container {
float: right;
width: 280px;
padding: 12px;
height: 100%;
box-sizing: border-box;
background: #fbfbfb;
overflow: auto;
}
fieldset span,
fieldset input,
fieldset select {
display: inline-block;
text-align: left;
}
fieldset input[type=text] {
width: calc(100% - 58px);
}
fieldset input[type=button] {
width: 100%;
text-align: center;
}
fieldset select {
width: calc(100% - 50px);
}
.field-line {
margin-top: 4px;
}
.field-inline {
display: inline-block;
vertical-align: middle;
}
fieldset label.field-inline {
width: 100px;
}
fieldset input.field-inline {
width: calc(100% - 100px - 12px);
}
.required {
color: red;
font-weight: bold;
}
#fields {
display: none;
}
#fields.show {
display: block;
}
#changesPanel {
width: 100%;
height: 300px;
}
(function (global) {
System.config({
transpiler: 'plugin-babel',
babelOptions: {
es2015: true,
react: true
},
meta: {
'*.css': { loader: 'css' }
},
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
'@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js',
'@mescius/spread-sheets-tablesheet': 'npm:@mescius/spread-sheets-tablesheet/index.js',
'@mescius/spread-sheets-react': 'npm:@mescius/spread-sheets-react/index.js',
'@mescius/spread-sheets-resources-ja': 'npm:@mescius/spread-sheets-resources-ja/index.js',
'@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js',
'react': 'npm:react/umd/react.production.min.js',
'react-dom': 'npm:react-dom/umd/react-dom.production.min.js',
'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: 'jsx'
},
"node_modules": {
defaultExtension: 'js'
},
}
});
})(this);