Collaborative Editing

4 Aug 20239 minutes to read

The collaborative editing support allows you to work at a spreadsheet collaboratively with other users. Multiple users can access to the the same spreadsheet simultaneously.

Dependencies

The following dependent script is required to use the collaborative editing support in spreadsheet.

<script src="https://cdnjs.cloudflare.com/ajax/libs/aspnet-signalr/1.1.4/signalr.js"></script>

Client configuration

To broadcast the data for every action in the spreadsheet, you need to transfer the data to the server through send method in actionComplete event and receive the same data by using the dataReceived method. In the dataReceived method, you need to update the action to the connected clients through updateAction method.

The following code example shows Collaborative Editing support in the Spreadsheet control.

@Html.EJS().Spreadsheet("spreadsheet").ActionComplete("actionComplete").Render()

<script>
    // For signalR Hub connection.
    var connection = new signalR.HubConnectionBuilder().withUrl('https://localhost:44385/hubs/spreadsheethub', {
        skipNegotiation: true,
        transport: signalR.HttpTransportType.WebSockets
    }).build();

    function actionComplete(args) {
        connection.send('BroadcastData', JSON.stringify(args));
    }

    connection.on('dataReceived', (data) => {
        var spreadsheetObj = ej.base.getComponent(document.getElementById('spreadsheet'), 'spreadsheet');
        var model = JSON.parse(data);
        spreadsheetObj.updateAction(model);
    });
    connection.start().then(() => {
        console.log('server connected!!!');
    }).catch((err) => console.log(err));

</script>
public IActionResult Index()
        {
            return View();
        }

Server configuration

To make the communication between the server to the connected clients and from clients to the server, you need to configure the signalR Hubs using the following code.

// For signalR Hub connection

var connection = new signalR.HubConnectionBuilder().withUrl('https://localhost:44385/hubs/spreadsheethub', { // localhost from AspNetCore service
    skipNegotiation: true,
    transport: signalR.HttpTransportType.WebSockets
  }).build();

Hub configuration

Initially create a AspNetCore project and add the hub file for sending and receiving the data between server and clients.

using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

namespace WebApplication.Hubs
{
    public class SpreadsheetHub : Hub
    {
        public async Task BroadcastData(string data)
        {
            await Clients.Others.SendAsync("dataReceived", data);
        }
    }
}

To configure the SignalR middleware by registering the following service in the ConfigureServices method of the Startup class.

    services.AddSignalR(e =>
    {
        e.MaximumReceiveMessageSize = int.MaxValue; // Option to increase message size for inserting image feature. By default, SignalR send messages up to 32 KB.
    });

To set up the SignalR routes by calling MapHub in the Configure method of the Startup class.

    app.UseEndpoints(endpoints =>

    {

        endpoints.MapRazorPages();

        endpoints.MapHub<SpreadsheetHub>("/hubs/spreadsheethub");

    });

For hosting the service, you may use the above code snippet or download and run the local service.

Prevent the particular action update for collaborative client

Using the action argument from the actionComplete event, you can prevent the particular action update for collaborative client.

The following code example shows how to prevent collaborative client from updating the format action.

@Html.EJS().Spreadsheet("spreadsheet").ActionComplete("actionComplete").Render()

<script>
    // For signalR Hub connection
    var connection = new signalR.HubConnectionBuilder().withUrl('https://localhost:44385/hubs/spreadsheethub', { // localhost from AspNetCore service
        skipNegotiation: true,
        transport: signalR.HttpTransportType.WebSockets
    }).build();

    function actionComplete(args) {
        if (args.action != 'format') {  // prevent the format action
            connection.send('BroadcastData', JSON.stringify(args)); // send the action data to the server
        }
    }

    connection.on('dataReceived', (data) => {
        var spreadsheetObj = ej.base.getComponent(document.getElementById('spreadsheet'), 'spreadsheet');
        var model = JSON.parse(data);
        spreadsheetObj.updateAction(model);
    });

    connection.start().then(() => {
        console.log('server connected!!!');
    }).catch((err) => console.log(err));

</script>
public IActionResult Index()
        {
            return View();
        }

Perform import action for collaborative clients

Using the action argument from the actionComplete event, you can identity whether the import action is performed or not. If the action is import, then you need to send the response data to the server and also update the same to the collaborative clients.

The following code example shows how to perform the import functionality for collaborative clients.

@Html.EJS().Spreadsheet("spreadsheet").ActionComplete("actionComplete").Render()

<script>
    // For signalR Hub connection
    var connection = new signalR.HubConnectionBuilder().withUrl('https://localhost:44385/hubs/spreadsheethub', { // localhost from AspNetCore service
        skipNegotiation: true,
        transport: signalR.HttpTransportType.WebSockets
    }).build();

    function actionComplete(args) {
        if (args.action != 'format') {  // prevent the format action
            connection.send('BroadcastData', JSON.stringify(args)); // send the action data to the server
        }
        else {
            // Send the action data to the server for other than import actions.
            connection.send("BroadcastData", JSON.stringify(args));
        }
    }

    connection.on('dataReceived', (data) => {
        var spreadsheetObj = ej.base.getComponent(document.getElementById('spreadsheet'), 'spreadsheet');
        var model = JSON.parse(data);
        if ( model['action'] === undefined || '') {
            // Load the imported excel file data as JSON to the connected clients.
            var jsonData = { Workbook: model };
            spreadsheetObj.openFromJson({ file: jsonData });
        }
        else {
            // Update the action details to the connected clients.
            spreadsheetObj.updateAction(model);
        }
        spreadsheetObj.updateAction(model);
    });

    connection.start().then(() => {
        console.log('server connected!!!');
    }).catch((err) => console.log(err));

</script>
public IActionResult Index()
        {
            return View();
        }