Composing a Fusion View Out of Components

Vaadin Fusion uses the Lit library for building views. Views are constructed using components from the Vaadin component library, HTML, and CSS.

This chapter covers:

  • Vaadin component basics.

  • Building reactive UIs.

  • Lit basics.

  • Constructing a view with Vaadin components and Lit.

Vaadin UI Components

Vaadin includes over 40 UI components to help you build apps faster. By using ready-made building blocks, you are able to focus more of your time on building end-user functionality.

Vaadin components are custom HTML elements that are registered with the browser. They are based on W3C web component standards. Once loaded, you can use the custom HTML tags like any other HTML.

The components have light and dark themes, and you can further customize them through CSS variables to fit your brand.

An Introduction to Building Reactive UIs

Building reactive views and components may take some getting used to, if your background is in Vaadin Flow, jQuery, or another imperative model. Reactive views have fewer moving parts and because of this they are easier to understand and debug. In reactive UI programming, your UI is a function of the component’s state. Whenever the state changes, the UI is re-rendered.

In imperative UI programming, there are two states: the data model and the UI. It is your responsibility as a developer to keep them in sync. In reactive programming, there is only one state: the data. The UI always reflects that state.

public class ImperativeView extends Div {
 H1 header = new H1();
 ListItem email = new ListItem();
 ListItem phone = new ListItem();

 public ImperativeView() {
   add(header, new UnorderedList(email, phone));
 }

 // Update UI when contact changes
 public void setContact(Contact contact) {
   header.setText(String.format("Details for %s %s",
     contact.getFirstName(),
     contact.getLastName()));
   email.setText("Email: " + contact.getEmail());
   phone.setText("Phone: " + contact.getPhone());
 }
}

When building a UI imperatively, you need to keep references to elements so you can update their values when the underlying state (the Contact) changes.

export class ReactiveView extends View {
 @property({ type: Object })
 contact: Contact = {...};

 // Render is called automatically when contact changes
 render() {
   return html`
     <h1>Details for ${this.contact.firstName} ${this.contact.lastName}</h1>
     <ul>
       <li>Email: ${this.contact.email}</li>
       <li>Phone: ${this.contact.phone}</li>
     </ul>
   `;
 }
}

When building a UI reactively, you define a template using values from the state (Contact). Whenever the model changes, the template is automatically updated.

Tip
Avoid element references in reactive views

As a rule of thumb, you should not use document.querySelector or LitElement @query to get element reference in order to set values or properties imperatively. There is almost always a way to achieve the same with binding values through the template.

An Introduction to Lit

Lit is a lightweight and performant library for building reactive components with declarative templates.

If you are not familiar with Lit, please read the Lit basics article.

The Contact List View

The first view is the Contact list view. This view lists all the contacts in the system. Users can search, add, edit, and delete contacts on this view.

You initially focus only on the list view. You add the layout containing the header and sidebar later in the Navigation and parent layouts chapter.

In this and the next chapter, you create the needed layouts and components for the view. Then, in the chapter that follows, you create an endpoint for populating the view with data.

For this view, you need the following Vaadin components:

Importing Vaadin Components

Before you can use Vaadin components, you need to import them. Importing the components registers the custom HTML elements with the browser. It also helps the build tool to understand what components are being used, so it can optimize the resulting JavaScript to only include the needed components.

Add the following imports to the top of frontend/views/list/list-view.ts:

import "@vaadin/vaadin-text-field";
import "@vaadin/vaadin-button";
import "@vaadin/vaadin-grid";
import "@vaadin/vaadin-grid/src/vaadin-grid-column";

Defining the View Template

Now that the components are imported, you can use them in the template. The template is defined in the render() method.

Replace the contents of the render method with the following:

render() {
 return html`
   <div class="toolbar gap-s">
     <vaadin-text-field
       placeholder="Filter by name"
       clear-button-visible
     ></vaadin-text-field>
     <vaadin-button>Add Contact</vaadin-button>
   </div>
   <div class="content flex se-m h-full">
     <vaadin-grid class="grid h-full">
       <vaadin-grid-column path="firstName" auto-width>
         </vaadin-grid-column>
       <vaadin-grid-column path="lastName" auto-width>
         </vaadin-grid-column>
       <vaadin-grid-column path="email" auto-width>
         </vaadin-grid-column>
       <vaadin-grid-column
         path="status.name"
         header="Status"
         auto-width
       ></vaadin-grid-column>
       <vaadin-grid-column
         path="company.name"
         auto-width
         header="Company"
       ></vaadin-grid-column>
     </vaadin-grid>
   </div>
 `;
}
  • The text field is configured to show a placeholder text and a clear button.

  • Grid columns map to properties on an object based on the path attribute. By default, the header name is derived from the path. You can override it with header, for instance when binding to a nested object’s path.

  • The Vaadin Lumo theme comes with a set of CSS utility classes to simplify layouts and styling. Instead of having to write your own CSS for common tasks like adding margins, padding, setting sizes, and defining layouts, you can use utility CSS classes like flex and h-full. You can browse all the classes in node_modules/lumo-css-framework/all-classes.css. The classes follow Tailwind CSS classnames.

Adding CSS Classes to The View Component

Until now, you have only added CSS class names to HTML elements within the template. You also need to add classes to the <list-view> element itself. The easiest way to do this is to use the connectedCallback lifecycle callback.

Add the following method to the ListView class:

connectedCallback() {
 super.connectedCallback();
 this.classList.add(
    'box-border',
    'flex',
    'flex-col',
    'p-m',
    'spacing-b-s',
    'w-full',
    'h-full'
  );
}

Here, you apply the following styles:

  • box-border - include padding in the height and width.

  • flex, flex-col - a vertical flex layout.

  • p-m - medium padding.

  • spacing-b-s - spacing bottom, small.

  • w-full, h-full - full width and height.

It is important to remember to call super.connectedCallback() whenever you override connectedCallback() to ensure that Lit initializes the component. Save your view file and you should now see this in your browser (start the development server with mvn if you don’t have it running):