Vaadin 8 and Vue.js — a smooth migration

We’re using a lot of of different technology stacks at XETICS GmbH. Not simply because it’s cool, but because we believe in choosing the right tool for the right job is always the best approach.

One of the things we’re using is Vaadin for developing the front end of XETICS LEAN. More precise, we’re still using Vaadin 8 which is based on Google Web Toolkit. Being a Java developer, GWT was a really handy tool to create nice and shiny web apps in a short amount of time. But times changed and because of various reasons, it is definitely a legacy technology nowadays. Hence, we decided we need to migrate.

The evaluation phase

TL;DR: we decided to write new UI components with Vue.js and integrate them in the existing Vaadin UI.

We’ve already put several years of hard work into our application. Simply rewriting everything would be the dream of every developer, of course. But having the costs in mind, it’s not a realistic approach. Hence, we decided that we want to have a smooth migration where we’ll create new components in the new technology and integrate them into existing Vaadin UI. After gaining enough experience, we’re going to remove the whole Vaadin 8 stack.

In the end, the following options were on the table:

  • Migrating to Vaadin 10+
  • Angular
  • Vue.js

Starting with Vaadin 10, they’ve managed to remove the underlying GWT stack and migrated to Web Components. This makes it much easier to create custom client side widgets. But the biggest disadvantage of the old GWT stack stays: manipulating the DOM from the server side.

The problematic thing about this approach is that it generates much more load on the server side than handling it on the client side. While this is totally fine when you deploy your app on your own server infrastructure, it can get easily expensive deploying it to cloud service providers like Amazon Web Services. But that’s exactly were it will run in the end.


Angular doesn’t have this huge disadvantage. It also comes with TypeScript which is a bit more self explaining to Java developers than plain JavaScript is, IMHO. Plus, we already have experience with it. The front end of our EASY app is written in it.

So it seems to be a no-brainer to use it. But during the proof of concept, we realized that the component based approach is causing some recurring boilerplate code you have to write just for having a single small view. As we want to migrate view after view, this boilerplate would grow and grow. More important, we fear that because of that, the approach won’t be accepted by our developers.

Another concern was the size of the library which will be load by the client side. Obviously, the smaller the library overhead shipped to the client side, the better the (felt) user experience will be. Although the Angular people did huge improvements with AOT compilations and tree-shaking in the past, the overall size of Vue is still about the half of Angular (~30KB gzipped compared to ~65KB gzipped).

https://vuejs.org/v2/guide/comparison.html


Hence, we’ve chosen Vue.js as the right tool for this job. We’ve already done some evaluation for the EASY app. So we are sure that this technology will be accepted by everyone in our company.

You might miss React. We already had some experience with it in the past. Our developers weren’t really satisfied with it. These feelings also stayed after we had a look at it again. It’s nothing technical against React, just personal preferences. If you’re considering to get rid of your old front end stack, it should be definitely in your list of technologies to evaluate. It might be the right tool for your use case!


Think about the coding infrastructure

One thing you have to consider when mixing different technologies together is the infrastructure and tools which are needed to develop, build, test and deploy the whole application. For the sake of simplicity, you can either put the new Vue.js code into the existing project or separate it in own projects.

As we’re managing our code base in GitLab, it was pretty easy to create new projects for every new view. With that approach we got a nice separation for free and are ensuring that the views won’t share hard dependencies to each other. This makes it much easier in the future to strip down the application in a way that you are just delivering what’s needed, nothing more.

In addition, we can create some kinds of micro teams for each new Vue.js project which can work on it totally independent from the whole application stack. We can even develop small improvements or bug fixes for the existing Vaadin UI without having to coordinate everything with the new development. Once the Vue.js app is done, it simply replaces the old one smoothly.

There is one downside we’re facing with after a while; the maintainability of the dependencies of the different Vue.js projects. As the JavaScript world moves forward much faster than the Java world this can be a mess. We don’t have the perfect solution for it until now. One idea is to maintain a single Vue project as a XETICS specific library project which provides all kind of infrastructure and dependency to our other projects.


How to integrate

There are multiple ways to integrate the new code with the existing one. As always, there is no right or wrong and it always depends on the use case. We elaborated three ways which fit into our use cases. All have advantages and disadvantages:

https://vuejs.org/v2/guide/comparison.html

https://vuejs.org/v2/guide/comparison.html


AbstractJavaScriptComponent

Integrating via the AbstractJavaScriptComponent has some peculiar charm as you can pass the data from the server side Java code directly to the UI without providing web services for it. Much more important, you don’t need to care about securing the communication because it will be handled completely by Vaadin itself.

The downside of it is that you have to create boilerplate code for each new Vue.js component you want to glue to the Vaadin UI. That’s what we already disliked about the Angular solution. Nevertheless, let’s dig into it. I will explain it with a color picker example which synchronizes with Vaadin’s color picker component.


First of all, we need some code part which acts like glue between the Vaadin code and our Vue.js code. The Vaadin people are calling this the JavaScript client-side connector. It defines a global initialize function based on the name of the server side component (the Java class which extends from the AbstractJavaScriptComponent; we’ll come to that later).

window.com_xetics_mes_vue_VueComponent = function () {
var container = this.getElement();
var connector = this;
var mapState = Vuex.mapState;
var model = {
message: ""
}
const store = new Vuex.Store({
state: model,
actions: {
callVaadin(state, payload) {
connector.onSyncColorPicker(payload)
},
onSyncColor(state, payload) {
connector.onSyncColorPicker(payload);
}
},
mutations: {
setMessage ( state, message) {
state.message = message;
}
}
})
console.debug("store: ", store);
var vm = window.ColorPicker.createColorPicker('#app', model, store);
console.debug("vue: ", vm);
this.onStateChange = function () {
connector.onSyncColorPicker(this.getState().message)
console.debug("new color: " + this.getState().message);
}
};

The easiest way of having bidirectional communication is using a state management solution. This is where Vuex joins the game.

https://vuejs.org/v2/guide/comparison.html

The JavaScript snippet shown above will initialize a Vuex store in which we store the state transitions of the Vue.js color picker. Instead of mutating the state directly, we’ll use the concept of Actions to commit the mutation. The onSyncColorPicker function will be added by the Vaadin component and use a consumer to sync both states.


Now we have the glue, let’s have a look for the server-side component.

@StyleSheet({"colorPicker.css"})
@JavaScript({"vue.js","es6-promise.auto.js", "vuex.js", "bundle.js", "vaadin-injector.js"})
public class VueComponent extends AbstractJavaScriptComponent {
private Consumer<Color> onColorChangedConsumer;
public VueComponent() {
setId("app");
addFunction("onSyncColorPicker", arguments > {
Notification.show("Message from VUE: " + arguments.get(0).asString());
if (onColorChangedConsumer != null) {
int r = (int)arguments.getObject(0).getObject("rgba").getNumber("r");
int g = (int)arguments.getObject(0).getObject("rgba").getNumber("g");
int b = (int)arguments.getObject(0).getObject("rgba").getNumber("b");
int a = (int)arguments.getObject(0).getObject("rgba").getNumber("a");
Notification.show("Color Changed to r:" + r + ", g:" + g + ", b:" + b + ", a:" + a);
onColorChangedConsumer.accept(new Color(r, g, b, a));
}
});
}
public void onColorChanged(Consumer<Color> onColorChangedConsumer) {
this.onColorChangedConsumer = onColorChangedConsumer;
}
@Override
public VueState getState() {
return (VueState)super.getState();
}
}

As already mentioned, we‘re adding the onSyncColorPicker function here which accepts the consumer to change the color of the Vaadin color picker. Furthermore, we need to add the Vue.js dependencies via the @JavaScript annotation. It’s important to know, that the order of the scripts matter. Vaadin will initialize those in the given sequence. Hence, place your needed dependencies first, the connector script last.


In addition, we need to create a shared state class on the server side. It has to extend from JavaScriptComponentState.

public class VueState extends JavaScriptComponentState {
public String message = "";
}
view raw VueState.java hosted with ❤ by GitHub

In our case, it just provides a message object to display a notification when the state was changed from the JavaScript side.

Finally, we just need to construct an instance of our VueComponent , add a change listener and let the Vue magic happen.

Don’t integrate at all

The total opposite of the above presented solution is to not integrate the Vue application at all. What does that mean?

In our case it means, we just place a button somewhere in the existing Vaadin UI which opens the app in a browser tab. Pretty simple, through we need to care about the authentication now.

val taktMonitorButton = Button().apply {
icon = MaterialIcons.ALARM
caption = Messages.getString("EditMasterDataLayout.taktMonitorButton.caption")
id = EQUIPMENT_GROUP_TAKT_MONITOR_BUTTON_ID
this.addStyleName(XeticsLeanStyles.OUTLINE_ICON_BUTTON)
this.addClickListener { openTaktMonitor(equipmentGroupVM.equipmentGroup) }
}
private fun openTaktMonitor(equipmentGroup: EquipmentGroup) {
val language = Messages.getLocale()
val taktMonitorUrl = "$taktUrl/?group-id=${equipmentGroup.id}&group-name=${equipmentGroup.name}&language=$language#/"
UI.getCurrent().page.open(taktMonitorUrl, "_blank")
}

All needed information are hand over via request parameters to the VueJS application.

Clicking the button will open the App in a pop up window
The not integrated Vue App

Integrate via iFrame

A nice mixture of both solutions is to integrate the Vue application via iFrame into the existent Vaadin code base. Although the view is currently integrated, we can still provide it easily as a standalone component.

The downside is, that you have to care about securing the communication between both technologies. Showing an additional login dialog when you open the the tab would be a UX catastrophe. To solve that problem, I would suggest to use a token based authentication. As this article isn’t about security, I will just point out to jjwt, an implementation of JSON Web Tokens for Java created by Okta.

https://vuejs.org/v2/guide/comparison.html

Vue app (inside red rectangle) integrated in Vaadin code

The Vaadin implementation itself is pretty easy. We’ll simply use Vaadin’s BrowserFrame component to which the external URL of the Vaadin application is hand over.

import com.vaadin.ui.BrowserFrame
import com.vaadin.ui.TabSheet
import com.xetics.mes.core.util.isReachable
import com.xetics.mes.ui.common.util.TabsDispatcher
class EquipmentGroupTabSheet(private val equipmentGroupVM: EquipmentGroupVM)
: TabSheet() {
private val tabsDispatcher = TabsDispatcher(this)
privatel val equipmentGroupUserStationAssignment = BrowserFrame(Messages.getString(
"EquipmentView.group.userStationAssignment")).apply {
id = userStationAssignmentId
this.setSizeFull()
}
init {
addSelectedTabChangeListener { event: SelectedTabChangeEvent ->
tabsDispatcher.dispatchComponentTabSelectionToRunnable(event.tabSheet.selectedTab)
}
this.equipmentGroupVM.userStationAssignmentUrl.observe {
if (!isReachable(it)) {
Notification(Messages.getString("Global.service.notReachable"), Notification.Type.ERROR_MESSAGE)
.show(Page.getCurrent())
}
equipmentGroupUserStationAssignment.source = ExternalResource(it)
}
}
}

Conclusion

As seen above, the integration is no sorcery but a matter of your needs. For us, the BrowserFrame solution is the way to go.

Finally, I want to make clear again, that this article is not a rant against Vaadin. Their framework is still a nice and modern solution for Java developers.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create your website with WordPress.com
Get started
%d bloggers like this: