The Vaadin Collaboration Engine is a set of features that enables end users of your Vaadin application to collaborate with each other in real time within the application. It was built to help people work together when not sharing an office space. It’s included in all commercial Vaadin subscription tiers.
You can try a Collaboration Engine demo here.
What is included in Collaboration Engine?
Even though it is a new feature, Vaadin Collaboration Engine is is ready for production and currently offers:
- A high-level API for specific use cases, like those in this post.
- A low-level Topic APIs for custom collaboration features.
- Collaboration Avatar Group component that populates an avatar group with all the people present in the view, component or application.
- CollaborationBinder that allows everyone using a form to see and share the activities of other users in real time.
We’re adding features, such as a live discussion and notifications, to the full release. See the release roadmap for details.
Demo
The demo app allows people to see who else is working on the form and edit it together in real time. It implements the new Collaboration Engine features in the contact form built in our CRM tutorial app. You can build an app like it by following our comprehensive tutorial series.
In this post, we explain the Collaboration Engine features through text and code examples. Watch this webinar recording for a live coding demo.
Are you interested in implementing Collaboration Engine for your Vaadin app? Head over to the product page for pricing and availability.
Implementing Collaboration Engine features
Let’s take a look at how the Collaboration Engine features were implemented in the demo.
Installing Collaboration Engine
Adding dependencies
To install Collaboration Engine, add the following dependency in the <dependencies>
tag of your pom.xml
. See the release notes for the latest version number (currently 2.0.0):
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>collaboration-engine</artifactId>
<version>2.0.0</version>
</dependency>
This downloads and installs the new Collaboration Engine components.
NOTE: Collaboration Engine will not work in production mode without a valid license! See pricing and request access here. All commercial Vaadin subscriptions include a free quota of users.
The current version is available as a POM dependency. It is suitable if you deploy your Vaadin application on a single server, without clustering. The collaboration happens within the JVM, which means that end users will only see other users within the same server instance as they are on. However, we’re planning to release a Docker image next that will allow you to run it on a separate server within your infrastructure.
Enabling server push
To enable server push, add the @Push
annotation to your MainView.Java
:
@CssImport("./styles/shared-styles.css")
@Push
public class MainLayout extends AppLayout {
This opens a web socket that waits for data. When received, it immediately displays the data on the end user’s screen.
Showing who is editing the form
Showing users
Open ContactForm.Java
to add the collaboration features. First, create a new CollaborationAvatarGroup
for our avatars:
CollaborationAvatarGroup avatars;
An Avatar
depicts each user in the app and an AvatarGroup
consists of multiple avatars. CollaborationAvatarGroup
binds an AvatarGroup
to Collaboration Engine automatically, eliminating the need for boilerplate code to build the logic.
Next, instantiate the new group. This requires two parameters:
UserInfo
: A class that identifies who each user is (picture and name). This requires a unique user id to identify what data belongs to whom. We assign “user” as theuser id
for now."app"
: A string that identifies our topic id. A topic id is an identifier in Collaboration Engine for sessions in which users can collaborate. We’ll return to this in more detail in the next step.
public ContactForm(List<Company> companies) {
addClassName("contact-form");
UserInfo UserInfo = new UserInfo(userid: "user");
avatars = new CollaborationAvatarGroup(userInfo, “app”)
Finally, add the new avatars
component to the layout:
add(avatars,
firstName,
lastName,
email,
status,
company,
createButtonsLayout()
);
If you build and run the app at this point, you will see an avatar above the form labelled “Anonymous User” (on mouseover).
You can also set a name:
UserInfo UserInfo = new UserInfo(userid: "user");
UserInfo.setName("Mikael Sukoinen");
Now it can share this name with other users.
NOTE: Collaboration Engine automatically abbreviates and uses initials if there is no user avatar (for example, Mikael Sukoinen = MS).
TIP: Please refer to the technical documentation to see how to load user names and images from the backend.
Specifying which row is edited
Now we come back to the topicId
that we previously named “app”. You can compare topicId
to a room in which everyone can hear and see one another. Currently, all users use the same “app” topic id, which means that they can all see each other, regardless of what part of the application they have open. What we want to do instead, is to use a distinct topic id for each row in the grid, so that everyone viewing a specific row is in a “separate room”.
We achieve this by providing an identifier that specifies the row. These identifiers are always strings, and you can choose how to format them - they should be unique and distinct from each other, so you know what they are for.
Start by changing the topic id from "app"
to null
to show that we don’t have a topic yet:
avatars = new CollaborationAvatarGroup(user, topicid: )
Next, extract the "contact/"
topic as a string to be able to reuse it. Then use setTopic
to set the topic to "contact/"
for the Avatars
:
public void setContact(Contact contact) {
this.contact = contact;
binder.readBean(contact);
String topic = “contact/” + contact.getId();
avatars.setTopic(topic);
binder.setTopic(topic, () -> contact);
}
The topicid
will be set to "contact/"
when editing it with id 2 etc.
Finally, add one more check:
public void setContact(Contact contact) {
this.contact = contact;
binder.readBean(contact);
if(contact != null){
avatars.setTopic(“contact/” + contact/getId());
} else {
avatars.setTopic(null);
}
}
This defines the topic. If the contact
is anything other than null
, the specific topic is used. If the contact
is null
, it is removed.
Now you can see who is actively editing a row in the form. You can test this by opening the demo in two browser windows. Note how the two users appear and disappear when selecting and deselecting a row.
Enabling collaborative editing
Adding CollaborationBinder
The next step is to enable collaboration between users when editing the form.
We used Binder
to bind the contact entity to the form. To be able to achieve collaboration on that entity, Vaadin created an extended version of Binder
called
CollaborationBinder
.
First, replace Binder
with CollaborationBinder
and add Contact
as the bean validation type:
CollaborationAvatarGroup avatars;
CollaborationBinder<Contact> binder;
private Contact contact;
Binder
needs certain information for initialization. Initialize it under avatars with the Contact
class and user
topicid:
avatars = new CollaborationAvatarGroup(userInfo, topicid: null;)
binder = new CollaborationBinder<>(Contact.class, userInfo);
Next, scroll to where the readBean
method is used to populate the fields. As you will notice, it’s crossed out. The readBean
method is deprecated by the Binder
. We provide a new method binder.setTopic
that takes a topicid
and the initial object that you want to edit.
Add the Binder
method and set the string topic for it:
public void setContact(Contact contact) {
this.contact = contact;
binder.readBean(contact);
if(contact != null){
String topic = “contact/” + contact.getId();
binder.setTopic(topic);
avatars.setTopic(topic);
} else {
avatars.setTopic(null);
}
}
Next, add the supplier of the initial object:
public void setContact(Contact contact) {
this.contact = contact;
binder.readBean(contact);
if(contact != null){
String topic = “contact/” + contact.getId();
binder.setTopic(topic, () -> contact);
avatars.setTopic(topic);
} else {
avatars.setTopic(null);
}
}
What this means is that if no one edits this item in the CollaborationBinder
, it uses data from the Contact
bean. But if someone has already edited the object, then we use the data in Collaboration Engine. We don’t want to overwrite with the data from the database, but rather to continue from where the other person left off.
And finally, comment out the old readBean
method:
public void setContact(Contact contact) {
this.contact = contact;
//binder.readBean(contact);
if(contact != null){
String topic = “contact/” + contact.getId();
binder.setTopic(topic, () -> contact);
avatars.setTopic(topic);
} else {
avatars.setTopic(null);
}
}
Now, the field that the user is working on becomes highlighted and shows the name of the user typing in it when you hover over it. Other users can now see who is working, which field they are working on, and the changes they are making. If the user has selected the field, but is currently in a different browser window, the highlight is removed to indicate that the user is not actively editing it.
NOTE: If two or more people try to edit the same field simultaneously, the data of the last user to blur the input field will be stored.
Are you ready to get started with Collaboration Engine? Head over to vaadin.com/collaboration to request access!