Skip to content

Commit bb042eb

Browse files
[add] multiuser backend and customization of server events
1 parent 278e5f8 commit bb042eb

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

docs/guides/working_with_server.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,161 @@ You need to include **RestDataProvider** into the **Event Bus** order via the [*
8787
In this snippet you can see how to connect **RestDataProvider** to the **Go** backend and load server data dynamically:
8888

8989
<iframe src="https://snippet.dhtmlx.com/f25y0809?mode=js&tag=kanban" frameborder="0" class="snippet_iframe" width="100%" height="500"></iframe>
90+
91+
## Multiuser backend
92+
93+
Project management tools, such as our Kanban, are highly sought after by businesses of all sizes. Considering this, it is important to provide a seamless user experience for multiple users. Our new feature allows users to efficiently manage the same cards on the Kanban board in real-time, without the need for page reloads. As a result, end-users can collaborate and stay up-to-date with one another's actions, enhancing productivity and overall satisfaction.
94+
95+
To implement a multiuser backend, you need to get authorization on the server before the Kanban initialization. For this, you can create the `login(url: string)` function:
96+
97+
~~~js {}
98+
const login = (url) => {
99+
var token = sessionStorage.getItem("login-token");
100+
if (token) {
101+
return Promise.resolve(token);
102+
}
103+
104+
return fetch(url + "/login?id=1")
105+
.then(raw => raw.text())
106+
.then(token => {
107+
sessionStorage.setItem("login-token", token);
108+
return token;
109+
});
110+
};
111+
~~~
112+
113+
This function only simulates authorization, and all users will be authorized with an ID of 1. After successful authorization, the server sends a token that needs to be used in every subsequent request to the server. To automate the token sending, the `RestDataProvider.setHeaders()` function is used. This function adds custom headers to the requests. By default, our server stores the token in the `"Remote-Token":<value>` header:
114+
115+
~~~js {}
116+
login(url).then(token => {
117+
// rest provider initialization
118+
const restProvider = new kanban.RestDataProvider(url);
119+
// set token as the custom header
120+
restProvder.setHeaders({
121+
"Remote-Token": "eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6ImJpem9uZSIsImlhdC...",
122+
});
123+
124+
// widget initialization...
125+
});
126+
~~~
127+
128+
After receiving the token, you should initialize the widget. It can be done in the following way:
129+
130+
~~~js {}
131+
// widget initialization...
132+
Promise.all([
133+
restProvider.getCards(),
134+
restProvider.getColumns(),
135+
restProvider.getRows(),
136+
]).then(([cards, columns, rows]) => {
137+
const board = new Kanban("#root", {
138+
cards,
139+
columns,
140+
rows,
141+
rowKey: "row",
142+
cardShape,
143+
editorShape,
144+
});
145+
146+
// save data from client to server
147+
board.api.setNext(restProvider);
148+
149+
// multiuser initialization...
150+
});
151+
~~~
152+
153+
After the widget initialization, you need to add WebSocket aimed to listen for events from the server. It can be done in the following way:
154+
155+
~~~js {}
156+
// multiuser initialization...
157+
158+
// get client handlers for server events
159+
const handlers = kanbanUpdates(
160+
board.api,
161+
restProvider.getIDResolver()
162+
);
163+
// connect to server events
164+
const events = new RemoteEvents(url + "/api/v1", token);
165+
// attach client handlers on server events
166+
events.on(handlers);
167+
~~~
168+
169+
- `handlers` - the client handlers that handle server events
170+
- `events` - the object that connects to the server and listens for all incoming events
171+
- `RemoteEvents.on(handlers)` - applies client handlers to server events
172+
173+
After integrating the multiuser backend into your app, you can simplify collaboration between users and enable them to keep track of any changes via the UI in a real time.
174+
175+
### Example
176+
177+
The snippet below shows how to configure the multiuser backend to track changes of other users in a real time:
178+
179+
<iframe src="https://snippet.dhtmlx.com/xw6g6qd6?mode=js" frameborder="0" class="snippet_iframe" width="100%" height="500"></iframe>
180+
181+
## Customization of server events
182+
183+
You can define your own logic for handling server events. For this purpose, you need to pass the **handlers** object to the `RemoteEvents.on(handlers)` method. The **handlers** object should have the following structure:
184+
185+
~~~js {}
186+
{
187+
"cards": cardsHandler: function(obj: any),
188+
"columns": columnsHandler: function(obj: any),
189+
"rows": rowsHandler: function(obj: any),
190+
}
191+
~~~
192+
193+
When any change occurs on the server, it returns the name of the modified element. These names can vary depending on the server logic.
194+
195+
The data updated on the client side will be placed in the **obj** argument of the `function(obj: any)` function. To specify an operation, there is a `type: string` field. It can take the following values:
196+
197+
- For **cards**: `"add-card"`, `"update-card"`, `"delete-card"`, `"move-card"`
198+
- For **columns**: `"add-column"`, `"update-column"`, `"delete-column"`, `"move-column"`
199+
- For **rows**: `"add-row"`, `"update-row"`, `"delete-row"`, `"move-row"`
200+
201+
In the following code snippet you can see the implementation details:
202+
203+
~~~js {}
204+
// initialize kanban
205+
const kanbanInstance = new kanban.Kanban(...);
206+
const restProvider = new kanban.RestProvider(url);
207+
const idResolver = restProvider.getIDResolver();
208+
const TypeCard = 1;
209+
const TypeRow = 2;
210+
const TypeCol = 3;
211+
212+
const cardsHandler = (obj: any) => {
213+
obj.card.id = idResolver(obj.card.id, TypeCard);
214+
obj.card.row = idResolver(obj.card.row, TypeRow);
215+
obj.card.column = idResolver(obj.card.column, TypeColumn);
216+
switch (obj.type) {
217+
"add-card":
218+
kanbanInstance.api.exec("add-card", {
219+
card: obj.card,
220+
select: false,
221+
skipProvider: true, // prevent the client from sending request to the server
222+
})
223+
break;
224+
// other operations
225+
}
226+
}
227+
228+
// add custom handlers
229+
const handlers = {
230+
cards: cardsHandler,
231+
};
232+
233+
const remoteEvents = new kanban.RemoteEvents(remoteEventsURL, token);
234+
remoteEvents.on(handlers);
235+
~~~
236+
237+
The `RestDataProvider.getIDResolver()` method returns a function that is necessary to synchronize client IDs with server IDs. When a new object (*card/column/row*) is created on the client side, the resulting object will have a temporary ID and a corresponding server ID in the store. The `idResolver()` function allows synchronizing the client ID with the server ID. This function has the following format: `idResolver(id: TID, type: number)`
238+
239+
The `type` argument is the type of model that takes the following values:
240+
241+
- `CardID` - 1,
242+
- `RowID` - 2,
243+
- `ColumnID` - 3
244+
245+
To prevent the request from being sent to the server, you need to use the `skipProvider: true` flag when calling the `kanbanInstance.api.exec()` method.
246+
247+
And the final step is to apply custom handlers to the server events. In this way you can create your own server event handlers.

0 commit comments

Comments
 (0)