Your search did not match any results.
Data Grid

Collaborative Editing

Documentation

This demo shows how users can use a SignalR service that broadcasts push notifications to edit the DataGrid's data in real time. You can open this page in another browser window and start editing to see this functionality. Changes made in one window are repeated in the other.

You can share the session ID (under the DataGrid) with other users to edit the DataGrid collectively.

Note that all changes are lost if you refresh the page because the SignalR service only broadcasts changes without saving them.

Copy to CodeSandBox
Apply
Reset
import React from 'react'; import DataGrid, { Column, Editing, Paging } from 'devextreme-react/data-grid'; import { TextBox } from 'devextreme-react'; import { HubConnectionBuilder, HttpTransportType } from '@aspnet/signalr'; import * as AspNetData from 'devextreme-aspnet-data-nojquery'; const BASE_PATH = 'https://js.devexpress.com/Demos/NetCore/'; const store = AspNetData.createStore({ key: 'ID', loadUrl: `${BASE_PATH}api/DataGridCollaborativeEditing/`, insertUrl: `${BASE_PATH}api/DataGridCollaborativeEditing/`, updateUrl: `${BASE_PATH}api/DataGridCollaborativeEditing/`, deleteUrl: `${BASE_PATH}api/DataGridCollaborativeEditing/`, onBeforeSend: function(operation, ajaxSettings) { ajaxSettings.data.sessionId = getSessionId(); } }), statesStore = AspNetData.createStore({ key: 'ID', loadUrl: `${BASE_PATH}api/DataGridStatesLookup` }); class App extends React.Component { constructor(props) { super(props); this.state = { sessionId: getSessionId() }; this.textBoxValueChanged = this.textBoxValueChanged.bind(this); } textBoxValueChanged(e) { setSessionId(e.value); this.setState({ sessionId: getSessionId() }); } render() { return ( <div id={'data-grid-demo'}> <DataGrid id={'gridContainer'} dataSource={store} showBorders={true} repaintChangesOnly={true} > <Paging enabled={false} /> <Editing refreshMode={'repaint'} mode={'cell'} allowUpdating={true} allowDeleting={true} allowAdding={true} /> <Column dataField={'Prefix'} caption={'Title'} width={55} /> <Column dataField={'FirstName'} /> <Column dataField={'LastName'} /> <Column dataField={'Position'} width={170} /> <Column dataField={'StateID'} caption={'State'} width={125} lookup={{ dataSource: statesStore, displayExpr: 'Name', valueExpr: 'ID' }} /> <Column dataField={'BirthDate'} dataType={'date'} /> </DataGrid> <div className={'options'}> <div className={'caption'}>Options</div> <div className={'option'}> <span>Session ID: </span> <TextBox id={'sessionId'} value={this.state.sessionId} onValueChanged={this.textBoxValueChanged} /> </div> </div> </div> ); } } const connection = new HubConnectionBuilder() .withUrl(`${BASE_PATH}dataGridCollaborativeEditingHub`, { skipNegotiation: true, transport: HttpTransportType.WebSockets }) .build(); connection.start() .then(function() { connection.on('update', function(key, data, sessionId) { sessionId === getSessionId() && store.push([{ type: 'update', key: key, data: data }]); }); connection.on('insert', function(data, sessionId) { sessionId === getSessionId() && store.push([{ type: 'insert', data: data }]); }); connection.on('remove', function(key, sessionId) { sessionId === getSessionId() && store.push([{ type: 'remove', key: key }]); }); }); var SESSION_KEY = 'dx-demo-collaborative-editing-session-id'; var sessionId; function getSessionId() { if (sessionId) { return sessionId; } var value = localStorage.getItem(SESSION_KEY) || generateRandomNumber(); setSessionId(value); return value; } function setSessionId(value) { sessionId = value; localStorage.setItem(SESSION_KEY, value); } function generateRandomNumber() { var max = 1000000, min = 0; return Math.floor(Math.random() * (max - min + 1)) + min; } export default App;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render( <App />, document.getElementById('app') );
<!DOCTYPE html> <html> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/19.1.6/css/dx.common.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/19.1.6/css/dx.light.css" /> <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <link rel="stylesheet" type="text/css" href="styles.css" /> <script type="text/javascript"> System.import('./index.js'); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
#sessionId { width: 100px; } .options { padding: 20px; margin-top: 20px; background-color: rgba(191, 191, 191, 0.15); } .caption { font-weight: 500; font-size: 18px; } .option { margin-top: 10px; } .option > span { position: relative; top: 2px; margin-right: 10px; } .option > .dx-widget { width: 500px; display: inline-block; vertical-align: middle; }
System.config({ transpiler: 'plugin-babel', paths: { 'npm:': 'https://unpkg.com/' }, defaultExtension: 'js', meta: { 'devextreme-aspnet-data-nojquery': { 'esModule': true } }, map: { 'react': 'npm:react@16/umd/react.development.js', 'react-dom': 'npm:react-dom@16/umd/react-dom.development.js', 'prop-types': 'npm:prop-types/prop-types.js', 'devextreme': 'npm:devextreme@19.1', 'devextreme-react': 'npm:devextreme-react@19.1', 'jszip': 'npm:jszip@3.1.3/dist/jszip.min.js', 'quill': 'npm:quill@1.3.6/dist/quill.js', 'devexpress-diagram': 'npm:devexpress-diagram', 'devextreme-aspnet-data-nojquery': 'npm:devextreme-aspnet-data-nojquery@2.4.4', // SystemJS plugins 'plugin-babel': 'npm:systemjs-plugin-babel@0/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0/systemjs-babel-browser.js', 'tslib': 'npm:tslib@1.6.1', '@aspnet/signalr': 'npm:@aspnet/signalr@1.0.0/dist/cjs/index.js' }, packages: { 'devextreme': { defaultExtension: 'js' }, 'devextreme-react': { main: 'index.js' } }, babelOptions: { sourceMaps: false, stage0: true, react: true } });