개요

테이블 시트에서는 AutoSync 및 Batch 모드를 사용하여 데이터 소스에 대한 자동 및 일괄 업데이트를 지원합니다.

기본적으로 테이블 시트는 로컬 데이터 관리자와 상호 작용합니다. 원격 데이터베이스와 변경된 데이터를 동기화하려면 먼저 AutoSync 또는 Batch 모드를 활성화하십시오. 이 데모에서는 Batch 모드를 사용합니다. AutoSync 모드 이 모드는 주로 빈도가 낮은 데이터 작업 시나리오에 적합합니다. 행 작업에서 버튼을 사용하거나 API를 사용하여 행과 상호 작용하면 해당하는 변경 사항이 있는 요청이 시작되어 즉시 서버로 전송됩니다. 표 초기화에서 AutoSync 모드를 활성화하려면 아래 샘플 코드를 참조하십시오. Batch 모드 이 모드는 데이터를 자주 조작하는 시나리오에 주로 적합합니다. 이 모드에서는 각 행 작업을 순서대로 저장한 후 모든 변경 사항을 컬렉션으로 패키지화한 다음 네트워크 리소스를 절약하기 위해 서버로 한 번에 전송합니다. Batch 모드를 활성화하고 표 초기화에서 BatchApiUrl를 지정하려면 다음 샘플 코드를 참조하십시오. 그런 다음 아래와 같이 모든 변경 사항을 제출하거나 취소할 수 있습니다. 변경 사항 가져오기 또한 변경 사항을 서버에 저장하기 전에 가져올 수도 있습니다. 요청 및 응답 작업 요청 유형 요청 데이터 응답 데이터 update POST 업데이트된 데이터 업데이트된 데이터 read GET 데이터 없음 레코드 배열 delete DELETE 삭제된 데이터 또는 데이터 배열 제한 없음 create POST 삽입된 데이터 삽입된 데이터 batch 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 * as ReactDOM from 'react-dom'; import GC from '@mescius/spread-sheets'; import "@mescius/spread-sheets-tablesheet"; import { SpreadSheets, Worksheet } from '@mescius/spread-sheets-react'; import '@mescius/spread-sheets-resources-ko'; GC.Spread.Common.CultureManager.culture("ko-kr"); import './styles.css'; var tableName = "Employee"; var baseApiUrl = getBaseApiUrl(); var apiUrl = baseApiUrl + "/" + tableName; var batchApiUrl = baseApiUrl + "/" + tableName + 'Collection'; var tablesheetName = 'MyTableSheet'; export function AppFunc() { let spread = null; const [changesContent, setChangesContent] = useState(""); const [tablesheet, setTablesheet] = useState(null); const [selections, setSelections] = useState([{row: 0, rowCount: 1, col: 0, colCount: 1}]); let initSpread = function(value) { spread = value; 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>Active Row Operations</legend> <div class="field-line"> <input id="remove" type="button" value="Remove" onClick={() => { removeRow() }} /> </div> <div class="field-line"> <input id="save" type="button" value="Save" onClick={() => { saveRow() }} /> </div> <div class="field-line"> <input id="reset" type="button" value="Reset" onClick={() => { resetRow() }} /> </div> </fieldset> <fieldset> <legend>Save All Rows</legend> <div class="field-line"> <input id="save-all" type="button" value="Save All" onClick={() => { saveAllRows() }} /> </div> </fieldset> <fieldset> <legend>Batch Operations</legend> <div class="field-line"> <input type="button" value="Submit" id="submit" onClick={() => { submitChanges() }} /> </div> <div class="field-line"> <input type="button" value="Discard" id="discard" onClick={() => { discardChanges() }} /> </div> </fieldset> <fieldset> <legend>Get Changes</legend> <div class="field-line"> <input type="button" value="Get Changes" 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\/learn-spreadjs\//)[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-tablesheet"; import '@mescius/spread-sheets-resources-ko'; GC.Spread.Common.CultureManager.culture("ko-kr"); 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>Active Row Operations</legend> <div class="field-line"> <input id="remove" type="button" value="Remove" onClick={() => { this.removeRow() }} /> </div> <div class="field-line"> <input id="save" type="button" value="Save" onClick={() => { this.saveRow() }} /> </div> <div class="field-line"> <input id="reset" type="button" value="Reset" onClick={() => { this.resetRow() }} /> </div> </fieldset> <fieldset> <legend>Save All Rows</legend> <div class="field-line"> <input id="save-all" type="button" value="Save All" onClick={() => { this.saveAllRows() }} /> </div> </fieldset> <fieldset> <legend>Batch Operations</legend> <div class="field-line"> <input type="button" value="Submit" id="submit" onClick={() => { this.submitChanges() }} /> </div> <div class="field-line"> <input type="button" value="Discard" id="discard" onClick={() => { this.discardChanges() }} /> </div> </fieldset> <fieldset> <legend>Get Changes</legend> <div class="field-line"> <input type="button" value="Get Changes" 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\/learn-spreadjs\//)[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$/ko/react/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <!-- SystemJS --> <script src="$DEMOROOT$/ko/react/node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('$DEMOROOT$/ko/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-resources-ko': 'npm:@mescius/spread-sheets-resources-ko/index.js', '@mescius/spread-sheets-react': 'npm:@mescius/spread-sheets-react/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);