Skip to content
Merged
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
15 changes: 15 additions & 0 deletions client/angular/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single

[*.md]
max_line_length = off
trim_trailing_whitespace = false
10 changes: 10 additions & 0 deletions client/angular/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/.angular
/.idea
/.vscode/*
!/.vscode/extensions.json
/dist
/node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.DS_Store
95 changes: 95 additions & 0 deletions client/angular/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Angular Spreadsheet Collaborative Editing

This example demonstrates real-time collaborative editing in the Syncfusion Angular Spreadsheet. Multiple users can edit the same workbook, and supported actions are synchronized through the Collaboration Server.

## Prerequisites

- Node.js
- Redis Server
- ASP.NET Core Redis Collaboration Server, or access to the configured hosted Collaboration Server

## Install dependencies

```bash
npm install
```

## Configure the Collaboration Server

The Collaboration Server URL is defined in:

```text
src/components/spreadsheet-editor/spreadsheet-editor.component.ts
```

Update `serviceUrl` when a different Collaboration Server endpoint is used.

## Run the application

```bash
npm start
```

Open the URL displayed by Angular, normally:

```text
http://localhost:4200/
```

When the page opens without an `id` query parameter, the client generates a room ID and updates the URL. Open the complete URL in another browser tab or window to join the same collaboration room.

Example:

```text
http://localhost:4200/?id=sample-room
```

## Project structure

```text
src/
|-- app/
| |-- app.config.ts
| |-- app.css
| |-- app.html
| `-- app.ts
|-- components/
| `-- spreadsheet-editor/
| |-- spreadsheet-editor.component.css
| |-- spreadsheet-editor.component.html
| |-- spreadsheet-editor.component.ts
| `-- spreadsheet-editor-adapter.ts
|-- index.html
|-- main.ts
`-- styles.css
```

## How it works

1. The Angular Spreadsheet starts with collaborative editing enabled.
2. The client obtains or generates a room ID from the URL.
3. The adapter imports the latest workbook and room version from the server.
4. `CollaborationClient` connects through SignalR and joins the room.
5. Local Spreadsheet actions are sent from the `actionComplete` event.
6. Remote actions are applied through `SpreadsheetEditorAdapter.applyRemoteAction`.

## Build

```bash
npm run build
```

## Important packages

- `@syncfusion/ej2-angular-spreadsheet`
- `@syncfusion/ej2-collaborator`
- `@microsoft/signalr`
- `@syncfusion/ej2-tailwind3-theme`

## Troubleshooting

If clients do not synchronize, verify that the Collaboration Server is reachable, Redis is running, all clients use the same room ID, and the browser console has no SignalR or CORS errors.

## License

Refer to the license file in the parent repository.
63 changes: 63 additions & 0 deletions client/angular/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"angular-spreadsheet-collaborative-editing": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"browser": "src/main.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": ["src/styles.css"]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "1MB",
"maximumError": "2MB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "angular-spreadsheet-collaborative-editing:build:production"
},
"development": {
"buildTarget": "angular-spreadsheet-collaborative-editing:build:development"
}
},
"defaultConfiguration": "development"
}
}
}
},
"cli": {
"analytics": false
}
}
31 changes: 31 additions & 0 deletions client/angular/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "angular-spreadsheet-collaborative-editing",
"version": "1.0.0",
"private": true,
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development"
},
"dependencies": {
"@angular/common": "^22.2.0",
"@angular/compiler": "^22.2.0",
"@angular/core": "^22.2.0",
"@angular/forms": "^22.2.0",
"@angular/platform-browser": "^22.2.0",
"@angular/router": "^22.2.0",
"@microsoft/signalr": "^9.0.0",
"@syncfusion/ej2-angular-spreadsheet": "35.1.37",
"@syncfusion/ej2-collaborator": "35.1.37",
"@syncfusion/ej2-tailwind3-theme": "35.1.37",
"rxjs": "~7.8.0",
"tslib": "^2.3.0"
},
"devDependencies": {
"@angular/build": "^22.2.0",
"@angular/cli": "^22.2.0",
"@angular/compiler-cli": "^22.2.0",
"typescript": "~6.0.2"
}
}
5 changes: 5 additions & 0 deletions client/angular/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ApplicationConfig } from '@angular/core';

export const appConfig: ApplicationConfig = {
providers: []
};
8 changes: 8 additions & 0 deletions client/angular/src/app/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
:host {
display: block;
width: 100%;
}

.app-container {
width: 100%;
}
3 changes: 3 additions & 0 deletions client/angular/src/app/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<main class="app-container">
<app-spreadsheet-editor></app-spreadsheet-editor>
</main>
11 changes: 11 additions & 0 deletions client/angular/src/app/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
import { SpreadsheetEditorComponent } from '../components/spreadsheet-editor/spreadsheet-editor.component';

@Component({
selector: 'app-root',
standalone: true,
imports: [SpreadsheetEditorComponent],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type {
ICollaborationActionData,
ICollaborationProvider
} from '@syncfusion/ej2-collaborator';
import { SpreadsheetComponent } from '@syncfusion/ej2-angular-spreadsheet';

interface ImportFileResponse {
sfdt: string;
version: number;
}

export class SpreadsheetEditorAdapter
implements ICollaborationProvider {
public currentRoomName: string = '';

public constructor(
private spreadsheet: SpreadsheetComponent,
private serviceUrl: string,
private currentUser: string
) {
this.serviceUrl = serviceUrl.endsWith('/')
? serviceUrl
: serviceUrl + '/';
}

public async loadFromServer(
fileName: string,
roomName: string
): Promise<void> {
const response: Response = await fetch(
this.serviceUrl + 'api/CollaborativeEditing/ImportFile',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ fileName, roomName })
}
);

if (!response.ok) {
throw new Error('Failed to load the workbook.');
}

const data: ImportFileResponse =
JSON.parse(await response.text());

this.currentRoomName = roomName;
this.spreadsheet.collaborativeEditingModule.updateRoomInfo(
roomName,
data.version,
this.serviceUrl + 'api/CollaborativeEditing/'
);
this.spreadsheet.collaborativeEditingModule.setLocalUser(
this.currentUser
);
this.spreadsheet.openFromJson({ file: data.sfdt });
}

public sendActionToServer(action: unknown): void {
if (!action) {
return;
}

this.spreadsheet.collaborativeEditingModule.sendActionToServer(
action
);
}

public applyRemoteAction(
action: string,
data: ICollaborationActionData
): void {
if (!data) {
return;
}

this.spreadsheet.collaborativeEditingModule.applyRemoteAction(
action,
data.payload
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:host {
display: block;
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ejs-spreadsheet
#spreadsheet
height="550px"
[enableCollaborativeEditing]="true"
(created)="created()"
(actionComplete)="actionComplete($event)">
</ejs-spreadsheet>
Loading
Loading