In particular, browsers offer a large panel of special HTML tags that they understand. For example, a
buttonis indeed always displayed as an actionable piece of UI, responding to mouse clicks as you might expect. Sometimes it goes further. Many people learn (the hard way) that
forms have a very specific behaviour when we “submit” them.
However, given the increasing complexity of web applications that we are seeing today, these built-in “components” are not enough. Frameworks such as Angular, React or Vue understood that a long time ago. Hence, they give developers the option to build their own components. But these components are bound to their framework. For example, you can’t easily user a React library of components from Angular (the other way is even worse). Luckily, though, browsers offer a tool to circumvent this problem: web components.
We are not going to talk about building these components. Rather, we are going to see how one can leverage web components in Scala, with the Laminar library.
Web Components are a technology enabling one to extend the native “components” (buttons, forms…) by creating new ones, as powerful as one can hope for. The fact that they are a web standard is also important for the future-proof guarantee it brings to the table.
The way web components work in detail is not relevant for us here. However, the way you use them is. Web component creators can define a number of attributes that control their behaviour, exactly the same way as you can set the
For example, imagine that you define a web component whose tag is
<stars-rating> with two attributes,
value. That way, you could insert in your HTML code (or manipulate the DOM to have it) the following:
<stars-rating max-stars=5 value=5></stars-rating>. And it would be rendered as a rating indicator, of 2 stars out of 5. This example is inspired by the Rating Indicator of the SAP UI5 library (more on that later).
With this example, you can see why any framework/library that is able to maniplulate the attributes of elements in the DOM, will be very effective at integrating web components. Even server-side rendering or jQuery can do that! And, in the case we are interested in, the Scala library Laminar can do it.
Laminar is a Scala.js library designed to manipulate the DOM from the comfort of your Scala code. The core design of Laminar is precisely to build a powerful DSL enabling you to manipulate what tags (elements) you insert into the DOM, and what attributes you give them.
Let’s come back to our
stars-rating example from earlier. In Laminar, you could imagine the following piece of code.
val ratingVar = Var(0)
_.value <-- ratingVar.signal.distinct,
_.maxStars := 5,
_.events.onChange.map(_.target.value) --> ratingVar.writer
This will keep in sync the number of stars selected by the user with the content of the variable
ratingVar. This variable can then be used as part of the submission for a form, for example.
If you know Laminar, you can notice that this syntax feels very familiar. And for a good reason. From the Laminar point of view, there is nothing that distinguishes between a native html tag and a web component.
You might still wonder where the
StarsRating function, or the
_.maxStars may be coming from. We are going to elaborate on that in the next section.
Declaring Web Components for Laminar
If you ever had the chance to take a look at Laminar’s code base, you probably saw that there are essentially two parts. The first part (and probably the most important) is the machinery. It defines all the ways end users can control the DOM, the tags, the attributes, the events… The other one is barely (so to speak) the declaration of all the built-in things that exist in the browser. That is, all the tags, all the events, all the attributes, all the style attributes, and so on and so forth. The main point is that the machinery is completely decoupled from the actual things that exist. We can easily add the definitions for any web components that we want to use.
How does it look like, concretely? Consider the following declaration for our
Let’s break this down step by step:
- We define that the html tag of the component is
stars-rating, so that Laminar knows that it has to insert a
<stars-rating>tag when we use it
- We tell Laminar all the special attributes that this component can have (in addition to all the usual attributes)
- We say that this web component has an
on-changeevent that can be triggered
- And finally, we define a function that we can call to actually create such web components
And that’s it! With these few lines of code, we can use the
stars-rating component within our Laminar code. Finally, let’s have a look at a concrete case study, with the SAP UI5 library.
A case study: the SAP UI5 library
As mentioned in the introduction, web components are very convenient, both for library maintainers and users. Because they don’t need to “agree” on a framework to use, being a web standard.
In particular, a Laminar user can choose any web component library they like, write these definitions on top, and they will be good to go. A library that I personnally enjoy a lot is the UI5 library from SAP. They have a high number of premium quality components, with outstanding documentation to back it up.
In fact, I like it so much that I wrote a library for Laminar users containing the definitions for all the components and features of this library.
If you are interested, you can take a look at these definitions by yourself. You will notice that writing those is straightforward. If you are curious, you can also take a look at the live demo, implemented with Laminar and the library.
The key take away of this blog post is that, if you are a Laminar user, and you are looking for ready to go components to use and fasten your development, you can search for any web component library out there. Besides SAP’s UI5, two other examples are Google’s Material UI and Shoelace.
If you are not a Laminar user but rather, let’s say, a Tyrian user (or any other Scala-js UI library), you might wonder whether the same kind of bindings can be done. The answer is definitevely yes!