Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
---
layout: post
title: Collaborative Editing in Vue Document editor control | Syncfusion
description: Learn about collaborative editing in Syncfusion Vue Document editor control of Syncfusion Essential JS 2 and more.
title: Collaborative Editing in Vue DOCX Editor control | Syncfusion
description: Learn about collaborative editing in Syncfusion Vue DOCX Editor control of Syncfusion Essential JS 2 and more.
platform: document-processing
control: Collaborative Editing
documentation: ug
domainurl: ##DomainURL##
---

# Collaborative Editing in Vue
# Collaborative Editing in Vue DOCX editor component

Allows multiple users to work on the same document simultaneously. This can be done in real-time, so that collaborators can see the changes as they are made. Collaborative editing can be a great way to improve efficiency, as it allows team members to work together on a document without having to wait for others to finish their changes.
Vue DOCX Editor (Document Editor) supports collaborative editing which allows multiple users to work on the same document simultaneously. This can be done in real-time, so that collaborators can see the changes as they are made. Collaborative editing can be a great way to improve efficiency, as it allows team members to work together on a document without having to wait for others to finish their changes.

## Prerequisites

- *Real-time Transport Protocol*: This protocol facilitates instant communication between clients and the server, ensuring immediate updates during collaborative editing.
- *Distributed Cache or Database*: Used to temporarily store the queue of editing operations.

### Real time transport protocol
### Real-time transport protocol

- *Managing Connections*: Keeps active connections open for real-time collaboration, allowing seamless communication between users and the server.
- *Broadcasting Changes*: Ensures that any edits made by one user are instantly sent to all collaborators, keeping everyone on the same page with the latest document version.
Expand All @@ -30,13 +30,13 @@ To support collaborative editing, it's crucial to have a backing system that tem

- ***Database***: With the same server configuration, it can handle up to 50 requests per second.

Using the distributed cache or database all the editing operations are queued in order and conflict resolution is performed using `Operational Transformation` algorithm.
Using the distributed cache or database all the editing operations are queued in order and conflict resolution is performed using `Operational Transformation` algorithm. Operational Transformation is a concurrency-control technique that reconciles concurrent edits by transforming the position and content of operations so all clients converge to the same final document state.

>**Tips**: To calculate the average requests per second of your application Assume the editor in your live application is actively used by 1000 users and each users edit can trigger 2 to 5 requests per second. The total requests per second of your applications will be around 2000 to 5000. In this case, you can finalize a configuration to support around 5000 average requests per second.
>**Tips**: To calculate the average requests per second of your application, assume the editor in your live application is actively used by 1000 users. Each user's edit can trigger 2 to 5 requests per second, so the total requests per second of your application will be around 2000 to 5000. In this case, you can finalize a configuration to support around 5000 average requests per second.

>**Note**: The above metrics are based solely on the collaborative editing module. Actual throughput may decrease depending on other server-side interactions, such as document importing, pasting formatted content, editing restrictions, and spell checking. Therefore, it is advisable to monitor your app’s traffic and choose a configuration that best suits your needs.

#### See Also
## See Also

- [Collaborative editing using Redis cache in ASP.NET Core](../collaborative-editing/using-redis-cache-asp-net-core)
- [Collaborative editing using Java](../collaborative-editing/using-redis-cache-java)
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,40 @@ domainurl: ##DomainURL##

# Collaborative editing in Vue DOCX Editor

Vue DOCX Editor (Document Editor) supports collaborative editing which Allows multiple users to work on the same document simultaneously. This can be done in real-time, so that collaborators can see the changes as they are made
[Vue DOCX Editor](https://www.syncfusion.com/docx-editor-sdk/vue-docx-editor) (Document Editor) supports collaborative editing which allows multiple users to work on the same document simultaneously. This can be done in real-time, so that collaborators can see the changes as they are made.

## Prerequisites

[Vue DOCX Editor](https://www.syncfusion.com/docx-editor-sdk/vue-docx-editor) (Document Editor).
The following are needed to enable collaborative editing in DOCX Editor.

- SignalR
- Redis

Configure CORS in the ASP.NET Core web service to allow the Vue application's origin (e.g., `http://localhost:8080` or your deployed domain) to call the API endpoints. Add CORS services and middle ware in `Program.cs`:

```csharp
// In Program.cs
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAllOrigins", policy =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
});
});

// ...later in the pipeline configuration
app.UseCors();
```

## SignalR

SignalR enables real-time communication by instantly sending and receiving document changes between clients and the server, ensuring seamless collaboration. In distributed environments, it can be scaled using Azure SignalR Service or a Redis backplane.

### Scale-out SignalR using Azure SignalR service

Azure SignalR Service is a scalable, managed service for real-time communication in web applications. It enables real-time messaging between web clients (browsers) and your server-side application(across multiple servers).
Azure SignalR Service is a scalable, managed service for real-time communication in web applications. It enables real-time messaging between web clients (browsers) and your server-side application (across multiple servers).

The following code snippet demonstrates how to configure Azure SignalR in an ASP.NET Core application using the `AddAzureSignalR` method in the "Program.cs" file of the web service project.

Expand Down Expand Up @@ -77,7 +95,7 @@ builder.Services.AddDistributedMemoryCache().AddSignalR().AddStackExchangeRedis(

In collaborative editing, Redis is used to store temporary data that helps queue editing operations and resolve conflicts using the `Operational Transformation` algorithm.

All editing operations are stored in the Redis cache. To prevent memory buildup, a `SaveThreshold` limit can be configured at the application level. For example, if the SaveThreshold is set to 100, up to twice that number of editing operations are retained in Redis per document. When this limit is exceeded, the first 100 operations (as defined by the save threshold) are removed from the cache and automatically saved to the source document.
All editing operations are stored in the Redis cache. To prevent memory buildup, a `SaveThreshold` limit can be configured at the application level. For example, if the SaveThreshold is set to 100, up to 200 editing operations (twice the SaveThreshold) are retained in Redis per document. When this limit is exceeded, the first 100 operations (as defined by the SaveThreshold) are removed from the cache and automatically saved to the source document, while the remaining operations stay in the cache for subsequent edits.

The configuration and storage size of the Redis cache can be adjusted based on the following considerations:

Expand All @@ -95,7 +113,7 @@ The configuration and storage size of the Redis cache can be adjusted based on t

Collaborative editing is built using three main components:

### Client (Vue Document Editor)
### Client (Vue DOCX Editor)

- Captures user edits in the document

Expand All @@ -121,15 +139,15 @@ Collaborative editing is built using three main components:

## Integrate collaborative editing in client side

### Step 1: Integrate Document Editor in Vue sample
### Step 1: Integrate DOCX Editor in Vue sample

Refer to the following documentation to get started with the [Vue Document Editor](https://help.syncfusion.com/document-processing/word/word-processor/vue/getting-started)
Refer to the following documentation to get started with the [Vue DOCX Editor](https://help.syncfusion.com/document-processing/word/word-processor/vue/getting-started)

### Step 2: Enable collaborative editing

To enable collaborative editing, inject `CollaborativeEditingHandler` and set the `enableCollaborativeEditing` property to true in the Document Editor.
To enable collaborative editing, inject `CollaborativeEditingHandler` and set the `enableCollaborativeEditing` property to true in the DOCX Editor.

The following code snippet demonstrates how to enable collaborative editing in the Document Editor.
The following code snippet demonstrates how to enable collaborative editing in the DOCX Editor.

```javaScript

Expand All @@ -153,7 +171,7 @@ export default {
data() {
return {
serviceUrl: 'http://localhost:5212/api/documenteditor/',
collborativeEditingServiceUrl: "http://localhost:5212/",
collaborativeEditingServiceUrl: "http://localhost:5212/",
collaborativeEditingHandler: null,
connection: null,
};
Expand All @@ -165,7 +183,7 @@ export default {
onCreated() {
// Inject collaborative editing module.
DocumentEditor.Inject(CollaborativeEditingHandler);
// Enable collaborative editing in Document Editor.
// Enable collaborative editing in DOCX Editor.
this.$refs.doceditcontainer.ej2Instances.documentEditor.enableCollaborativeEditing = true;
this.initializeSignalR();
this.loadDocumentFromServer();
Expand All @@ -180,28 +198,28 @@ export default {

To broadcast changes and receive updates from remote users, install the [Microsoft SignalR npm](https://www.npmjs.com/package/@microsoft/signalr) package in the Vue application.

The following code snippet demonstrates how to configure SignalR in the Document Editor.
The following code snippet demonstrates how to configure SignalR in the DOCX Editor.

```javaScript
import { HubConnectionBuilder, HttpTransportType, HubConnectionState } from '@microsoft/signalr';
// Component initialization logic
methods: {
initializeSignalR() {
// SignalR connection
this.connection = new HubConnectionBuilder().withUrl(this.collborativeEditingServiceUrl + 'documenteditorhub', {
this.connection = new HubConnectionBuilder().withUrl(this.collaborativeEditingServiceUrl + 'documenteditorhub', {
skipNegotiation: true,
transport: HttpTransportType.WebSockets
}).withAutomaticReconnect().build();
// Event handler for signalR connection
this.connection.on('dataReceived', this.onDataRecived.bind(this));
this.connection.on('dataReceived', this.onDataReceived.bind(this));

this.connection.onclose(async () => {
if (this.connection && this.connection.state === HubConnectionState.Disconnected) {
alert('Connection lost. Please relod the browser to continue.');
alert('Connection lost. Please reload the browser to continue.');
}
});
},
onDataRecived(action, data) {
onDataReceived(action, data) {
if (this.collaborativeEditingHandler) {
if (action == 'connectionId') {
// Update the current connection id to track other users
Expand Down Expand Up @@ -251,7 +269,7 @@ methods: {
window.history.replaceState({}, "", `?id=` + roomId);
}
var httpRequest = new XMLHttpRequest();
httpRequest.open('Post', this.collborativeEditingServiceUrl + 'api/CollaborativeEditing/ImportFile', true);
httpRequest.open('Post', this.collaborativeEditingServiceUrl + 'api/CollaborativeEditing/ImportFile', true);
httpRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
httpRequest.onreadystatechange = () => {
if (httpRequest.readyState === 4) {
Expand All @@ -274,7 +292,7 @@ methods: {
if (this.$refs.doceditcontainer) {
this.collaborativeEditingHandler = this.$refs.doceditcontainer.ej2Instances.documentEditor.collaborativeEditingHandlerModule;
// Update the room and version information to collaborative editing handler.
this.collaborativeEditingHandler.updateRoomInfo(roomName, data.version, this.collborativeEditingServiceUrl + 'api/CollaborativeEditing/');
this.collaborativeEditingHandler.updateRoomInfo(roomName, data.version, this.collaborativeEditingServiceUrl + 'api/CollaborativeEditing/');

// Open the document
this.$refs.doceditcontainer.ej2Instances.documentEditor.open(data.sfdt);
Expand All @@ -295,20 +313,21 @@ methods: {

Changes made on the client side must be transmitted to the server to be broadcast to other connected users.

The following code snippet demonstrates how to send changes to the server using the `contentChange` event in the Document Editor.
The following code snippet demonstrates how to send changes to the server using the `contentChange` event in the DOCX Editor.

```javaScript
methods: {
onContentChange(args) {
if (this.collaborativeEditingHandler) {
// Send the editing action to server
this.collaborativeEditingHandler.sendActionToServer(args.operations)
},
this.collaborativeEditingHandler.sendActionToServer(args.operations);
}
},
}
```
## Integrate collaborative editing in server side

### Step 1: Create the Document Editor web service project
### Step 1: Create the DOCX Editor web service project

Create an ASP.NET Core web service to handle server-side operations.

Expand Down Expand Up @@ -384,37 +403,37 @@ Add the following code to the file to manage SignalR groups using room names.
// Join group based on the room name and store the user details in Redis cache.
public async Task JoinGroup(ActionInfo info)
{
// Set the connection ID to info
// Set the connection ID on the info object
info.ConnectionId = Context.ConnectionId;
// Add the connection ID to the group
await Groups.AddToGroupAsync(Context.ConnectionId, info.RoomName);

//To ensure whether the room exixts in the Redis cache
//To ensure whether the room exists in the Redis cache
bool roomExists = await _db.KeyExistsAsync(info.RoomName + CollaborativeEditingHelper.UserInfoSuffix);
if (roomExists)
{
// Fetch all connected users from Redis
var allUsers = await _db.HashGetAllAsync(info.RoomName + CollaborativeEditingHelper.UserInfoSuffix);
var userList = allUsers.Select(u => JsonConvert.DeserializeObject<ActionInfo>(u.Value)).ToList();
//Send the exisiting user details to the newly joined user.

//Send the existing user details to the newly joined user.
await Clients.Caller.SendAsync("dataReceived", "addUser", userList);
}
// Add user to Redis

// Add user to Redis
await _db.HashSetAsync(info.RoomName + CollaborativeEditingHelper.UserInfoSuffix, Context.ConnectionId, JsonConvert.SerializeObject(info));

// Store the room name with the connection ID
await _db.HashSetAsync(CollaborativeEditingHelper.ConnectionIdRoomMappingKey, Context.ConnectionId, info.RoomName);
// Notify all the exsisiting users in the group about the new user

// Notify all the existing users in the group about the new user
await Clients.GroupExcept(info.RoomName, Context.ConnectionId).SendAsync("dataReceived", "addUser", info);
}

{% endhighlight %}
{% endtabs %}

#### 3. Handle user disconnection
#### 3. Handle user disconnection

The following code snippet demonstrates how to disconnect a connection using SignalR.

Expand Down Expand Up @@ -442,7 +461,7 @@ The following code snippet demonstrates how to disconnect a connection using Sig
if (pendingOps.Length > 0)
{
List<ActionInfo> actions = new List<ActionInfo>();
// Prepare the message fir adding it in background service queue.
// Prepare the message for adding it in background service queue.
foreach (var element in pendingOps)
{
actions.Add(JsonConvert.DeserializeObject<ActionInfo>(element.ToString()));
Expand Down Expand Up @@ -532,7 +551,7 @@ The following code snippet demonstrates how the operations are cached and update
try
{
ActionInfo modifiedAction = await AddOperationsToCache(param);
//After transformation broadcast changes to all users in the gropu
//After transformation broadcast changes to all users in the group
await _hubContext.Clients.Group(param.RoomName).SendAsync("dataReceived", "action", modifiedAction);
return modifiedAction;
}
Expand Down Expand Up @@ -643,6 +662,21 @@ For more details about code snippet, please refer this [link](https://github.com

This step implements a thread-safe, bounded queue to handle document save requests asynchronously without blocking the main application flow. It uses a channel-based approach with a fixed capacity to efficiently manage concurrent operations. The background service processes each save request by loading the document, applying changes, saving the updated file, and clearing the cache to maintain consistency.

Create the following service classes (place them in a `Services` folder):

- **`ISaveTaskQueue` / `SaveTaskQueue`**: A bounded `Channel<SaveInfo>`-based queue that exposes `QueueBackgroundWorkItemAsync(SaveInfo)` for the hub and controller to enqueue save requests.
- **`SaveTaskService` (`IHostedService` / `BackgroundService`)**: A long-running background worker that dequeues `SaveInfo` items, applies the actions to the source document, writes the file back, and clears the Redis cache for the room.

Register the connection multiplexer and queue in `Program.cs`:

```csharp
// In Program.cs
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("RedisConnectionString")));
builder.Services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
builder.Services.AddHostedService<QueuedHostedService>();
```

For more details about this code logic, please refer this [link](https://github.com/SyncfusionExamples/EJ2-Document-Editor-Collaborative-Editing/tree/master/Server%20side%20with%20distributed%20cache/ASP.NET%20Core/Using%20Redis/Services)

N> [View sample in GitHub](https://github.com/SyncfusionExamples/EJ2-Document-Editor-Collaborative-Editing/tree/master/Server%20side%20with%20distributed%20cache/ASP.NET%20Core/Using%20Redis).
Loading