Blog

Polymer 3 templates and the modernization to LitElement

By  
Ben Wilson
Ben Wilson
·
On Jan 7, 2021 1:48:01 PM
·

Lit_featured_image

There are a lot of reasons to be excited about the new Vaadin client-side model based on LitElement that was introduced in Vaadin 15 and is now being ported to the newest Vaadin 14 version. Some of the reasons include: the ability to develop with TypeScript, the collection of faster and lighter-weight components, the implicit binding between browser and server, and having access to the most up-to-date developments from the Chrome developer team behind LitElement.

But what if you have already invested in the previous version, Polymer 3? It turns out that upgrading a Vaadin application from Polymer templates to LitElement can be one of the most incidental modernizations you’ve ever tackled. In this article, we take you through what this means, covering the many options to modernize on an incidental basis and concluding with a few transformation hints.

A casual modernization

First, the good news: there are three ways in which you can spread your modernization from Polymer to LitElement. There is also little in the way of urgency, since Polymer is planned to be supported for all of the next releases of Vaadin, up to and including the next LTS version.

1: Migrate your templates in as many iterations as you need

Upgrading a Vaadin app’s templates from Polymer to LitElement is something you can undertake safely and gradually. The first way this manifests itself is that there is no requirement in Vaadin to be “all or nothing” in either Polymer or Lit.

As an example, say you have 20 views defined in Polymer today, and one of the views is about customer payments. You can dive into your customer-payments.js template and the server-side CustomerPayments.java files, make changes to fix these two files to correctly extend LitElement and LitTemplate, make no other changes to your project, check off “customer payments'' from your to-do list, and go to production.

2: Embrace Vaadin’s LitElement recommendations at your own pace

The second way you can upgrade gradually relates to how far you go in aligning the current Polymer artefacts with Vaadin’s developer recommendations for LitElement.

At Vaadin, we see a lot of potential in LitElement and have worked out a development model to enable developers to be highly productive, with the smallest chance of errors. This opinionated use of LitElement is based on a number of conventions and recommendations that you can ignore, if you prefer to prioritize reusing your existing Polymer templates as much as you can.

The thrust of these recommendations for developing in the Flow framework is to handle events, and associate event handlers with components on the server side in Java. To associate server-side components with their client-side counterparts, Vaadin provides the @Id annotation, which you should map to the id attributes of client-side elements.

The two options you have around these recommendations involve the use of TypeScript and the visibility of server-side methods in the client.

A lot of the LitElement documentation produced by Vaadin shows samples with TypeScript stored in .ts files. It’s technically also valid to write your lit-html template in JavaScript and store it in a .js file. This alternative approach is highlighted in tutorials like the one here. You can make use of the JavaScript compatibility to reduce the initial effort required to transform a Polymer template into LitElement by reusing the JavaScript that already works.

Client visibility of server-side methods that was simple in Polymer with the @EventHandler annotation (seen in many of the Vaadin starters), has been de-emphasized in the new LitElement recommendations. However, it’s technically achievable in LitElement as well using the @ClientCallable annotation in Java, combined with the this.$server.methodname syntax.

3: Upgrade templates and components at different speeds

Still using custom components or add-ons that are based on Polymer? These dependencies don’t have to hold you back from using Lit templates. Lit templates consist of Web Components, which with their standards-based makeup, can have a variety of origins. You can upgrade your Polymer templates to LitElement today and separately upgrade the components they host whenever it is convenient.

Begin and complete the upgrade with no configuration changes

One of the reasons this no-fuss gradual upgrade is possible, is because current Vaadin non-LTS version 17 and higher, and current LTS version 14.4 and higher, by default include the dependencies for both LitElement and Polymer. This also means you don’t have to change your build configuration to get started, or to clean up trailing dependencies after you have finished the final step.

One thing you may have to look out for is support for TypeScript in Vaadin 14, which is explained in a blog post here. Look out for announcements however, since this is tagged for inclusion in LTS 14.5.

What does change

While the concepts from Polymer mostly remain in LitElement, you’ll notice many syntax changes. There are a number of good sources that explain the new syntax (like here) so we don’t need to repeat that information here.

Other changes uniquely affecting Vaadin applications are the incompatibility of the @EventHandler annotation and the TemplateModel class.

1: @EventHandler

The @EventHandler annotation from Vaadin’s Polymer package made it possible to directly associate a client event with a server-side method invocation, from within the declarative (non-JavaScript) part of the template. As the user community started using this integration feature, we collected feedback on its use and decided to not make the feature available in LitElement templates.

In many cases, the use of @EventHandler can be replaced by setting the UI logic in the Java code instead. To do this, you need to assign an identifier to the component and map it to the Java counterpart. Alternatively, you can expose server-side methods using the @ClientCallable annotation and call those methods from the templates. This is what you often do when integrating an existing WebComponent into a Java API, for example.

If it looks like you are mixing and matching a lot of UI code from both the client and the server, it might make sense to look at the Fusion framework introduced in Vaadin 15. The more versatile Endpoints functionality replaces ClientCallable in Fusion. While they support a similar use case, they don’t offer the same line-by-line correspondence. A caveat for the eager: Endpoints have been available in non-LTS versions since Vaadin 15, but will only be made available in the LTS track after Vaadin 14.

Probably the best alternative to Endpoints in Vaadin 14 when you cannot use @EventHandler, is to use @ClientCallable. In the examples below, we take a look at how this works.

Let’s look at a simple use of an @EventHandler in Polymer:

// hello-world-view.js:
<vaadin-button on-click="clickOnServer">Date on server</vaadin-button>

 

// HelloWorldView.java:
@EventHandler
private void clickOnServer() {
Notification.show("Button click at: " + new Date().toString());
}

 

In this example, the information explicitly sent from client to server along with the invocation is an empty list of arguments. It was possible in the @EventHandler to retrieve more information about the event using implicit arguments added with @EventData annotated formal parameters. This shorthand is not available with @ClientCallable, so to preserve line by line equivalence, you have to push the relevant values out explicitly in arguments.

Here is a more complex example, in which we extract information relating to the selected item in a ListBox in the selected-changed event in TypeScript and pass it on to the @ClientCallable:

First the Polymer:

// hello-world-view.js
<vaadin-list-box on-selected-changed="lbChanged"></vaadin-list-box>

 

// HelloWorldView.java
@EventHandler
private void lbChanged(@EventData("event.detail.value") int index) {
Notification.show("selected item at index: " + index);
}

 

Transformed to LitElement gives us:

// hello-world-view.ts:
<vaadin-list-box 
@selected-changed="${(e:CustomEvent) => 
(this as any).$server.lbChanged( e.detail.value )}">
</vaadin-list-box>

 

// HelloWorldView.java:
@ClientCallable
private void lbChanged(int index) {
Notification.show("selected item at index: " + index);
}

 

This example shows good line-by-line equivalence with the Polymer version, but there is a downside. You can see that while the TypeScript part works, it is long and not easy to write or understand. You might find it preferable to replace @EventHandlers by associating server-side components through @Id and attaching server-side event handlers, rather than using @ClientCallables. Doing it this way is also in line with the new Vaadin recommendations.

Following this recommendation, the implementation of the above in LitElement would look as follows:

// hello-world-view.ts:
<vaadin-list-box id="listbox"></vaadin-list-box>

 

// HelloWorldView.java:
@Id
private ListBox listbox;

public HelloWorldView() {
listbox.addValueChangeListener((e) ->
{Notification.show("selected item at index: " + 
listbox.getElement().getProperty("selected"));
});
}

 

2: TemplateModel

Data binding for Polymer templates and the component was made easy by TemplateModel. For LitTemplates, we don’t have similar support: you should bind all dynamic content in your templates, using the Java API, to components you have mapped with identifiers to your Java counterparts.

For two-way data-binding (usually needed in forms), you can use Binder, just as you would with the pure Java approach. If you need more control in binding data to your templates, it might make sense to look into Fusion and its client-side version of the Binder.

Vaadin Fusion gives developers more granular control over the client-side LitTemplate UI logic and data binding. It is based on the idea of typed server-side endpoints that are available to TypeScript code in the browser. Endpoints work much like @ClientCallable methods in server-side Flow components, but are strongly typed, stateless, and are not attached to a particular component or view. Through Endpoints, you can pass any JavaBeans both ways between server-side Java and client-side TypeScript code in a secure and type-safe way. At compile time, the Vaadin Maven plugin will generate the TypeScript types for any formal parameters or return types of public Endpoint methods for you.

If you are on the LTS track, be aware that Endpoints are a part of Fusion that has not been backported to Vaadin 14, but will be re-introduced in the next LTS version. This could be a reason to move off Vaadin 14 and onto Vaadin 18.

If that’s not a viable solution for you and you still want to move forward with LitElement, the option that will give you the most flexibility in the future, is to do as much as possible using the Bean Validation specification, JSR 380. Using the same JavaBeans, you can generate both binders in Flow as BeanValidationBinders (which work in Vaadin 14) or binders in TypeScript, once you have generated the client-side models.

Conclusion

Development teams currently active with Polymer have a nice opportunity in Vaadin to start exploring how to introduce LitTemplates into their projects. Current and upcoming Vaadin releases include the dependencies you need for deploying both Polymer templates and Lit templates, so you can even do both at the same time. This gives your team the freedom to experiment, without the hassle of altering configurations and pipelines, and you can take it as gradually as makes sense for you.

If you’re on the LTS track, Vaadin 14.5 (planned in Q1 2021) aims to get up to speed with the non-LTS Vaadin 18, except for Fusion framework Endpoints and the generation of TypeScript client binders. This will mean equivalent full integration possibilities between server-side Java and client-side TypeScript or JavaScript. Upcoming releases of both 14 LTS versions and non-LTS versions of Vaadin will continue to have this dual support. Support in the next LTS version will include LitElement, with Polymer available as an option.

Ben Wilson
Ben Wilson
Ben joined Vaadin in 2016 after specializing many years in the automated modernization of large enterprise applications. Ben works in the Berlin office and is always looking for ways to salvage parts of old software to construct new, cutting-edge applications.
Other posts by Ben Wilson