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 CodePen
Apply
Reset
var DemoApp = angular.module('DemoApp', ['dx']); DemoApp.controller('DemoController', function DemoController($scope) { var BASE_PATH = "https://js.devexpress.com/Demos/NetCore/"; var store = DevExpress.data.AspNet.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(); } }); $scope.dataGridOptions = { dataSource: store, showBorders: true, repaintChangesOnly: true, paging: { enabled: false }, editing: { refreshMode: "repaint", mode: "cell", allowUpdating: true, allowDeleting: true, allowAdding: true }, columns: [ { dataField: "Prefix", caption: "Title", width: 55 }, "FirstName", "LastName", { dataField: "Position", width: 170 }, { dataField: "StateID", caption: "State", dataType: "number", width: 125, validationRules: [ { type: "required", message: "The State field is required." } ], lookup: { dataSource: DevExpress.data.AspNet.createStore({ "key": "ID", "loadUrl": BASE_PATH + "api/DataGridStatesLookup" }), displayExpr: "Name", valueExpr: "ID" } }, { dataField: "BirthDate", dataType: "date" } ] }; var connection = new signalR.HubConnectionBuilder() .withUrl(BASE_PATH + "dataGridCollaborativeEditingHub", { skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets }) .configureLogging(signalR.LogLevel.Information) .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 }]); }); }); $scope.textBoxOptions = { value: getSessionId(), onValueChanged: function(e) { setSessionId(e.value); } }; });
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" > <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" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script>window.jQuery || document.write(decodeURIComponent('%3Cscript src="js/jquery.min.js"%3E%3C/script%3E'))</script> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/19.1.5/css/dx.common.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/19.1.5/css/dx.light.css" /> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular.min.js"></script> <script>window.angular || document.write(decodeURIComponent('%3Cscript src="js/angular.min.js"%3E%3C\/script%3E'))</script> <script src="https://cdn3.devexpress.com/jslib/19.1.5/js/dx.all.js"></script> <script src="js/signalr/signalr-client.js"></script> <script src="https://unpkg.com/devextreme-aspnet-data@2.4.2/js/dx.aspnet.data.js"></script> <script src="data.js"></script> <script src="index.js"></script> <link rel="stylesheet" type="text/css" href="styles.css" /> </head> <body class="dx-viewport"> <div class="demo-container" ng-app="DemoApp" ng-controller="DemoController"> <div id="data-grid-demo"> <div id="gridContainer" dx-data-grid="dataGridOptions"></div> </div> <div class="options"> <div class="caption">Options</div> <div class="option"> <span>Session ID:</span> <div id="sessionId" dx-text-box="textBoxOptions"></div> </div> </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; }
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; }