Introduction to Hilla: The Complete Java Framework

Hilla combines a Java back-end built on Spring with a TypeScript front-end built with Lit, a fast and responsive framework for JavaScript. Hilla, which is based on Vaadin Fusion, is a unique animal in the Java ecosystem: something like Next.js for JavaScript, but with a Spring-based Java back-end. This article gets you started with Hilla, including how to scaffold a basic web application, create the front end, and add new components.

Hilla and Vaadin

The developers of Vaadin announced in January this year that they were rename Vaadin Fusion to Hilla. For developers already familiar with Fusion, only the name has changed. For developers new to Hilla, you’ll notice that the examples in this article use named packages and components for Vaadin. Vaadin Fusion packages will be renamed to Hilla in a future release.

Java web development with a responsive front end

Hilla brings together a responsive JavaScript front-end and a Spring Java back-end in a unified version. The examples in this article show you how these components work together to make Hilla a full-stack framework. To follow, you will need both Node.js (npm) and a recent JDK installed on your system. Make sure both node -v and java –version to work!

To get started, open your command line and scaffold a new project via npxas shown in List 1.

Listing 1. Scaffolding of a new project in Hilla

npx @vaadin/cli init --hilla foundry-hilla

Now, cd in the new directory and type ./mvnw (Where mvnw for windows). This command starts the Maven build. You will see output logging for the backend and frontend being built. Soon, the application will be operational in development mode.

hill sample IDG

Figure 1. Visit localhost:8080 and you should see your Hilla application running.

If you look at the filesystem you just built, you’ll see that the structure is split into a standard Maven structure and a frontend directory:

  • /project-root
    • /frontend
      • html
      • ts
      • ts
      • /stores
      • /themes
      • /views
    • /src
    • /target

The project root contains the Maven build file (pom.xml) which builds Java code from /src into /targetand calls the JavaScript build tool (fast.js) to create the frontend application contained in /frontend.

Build the front end

In Hilla, the front-end is booted from the /front-end/index.html, /front-end/index.tsand routes.ts files. Together, these files determine the routing and define the page content for the given route. The most instructive of these pages is routes.tsseen in listing 2.

Listing 2. Routes.ts

import { Route } from '@vaadin/router';
import './views/helloworld/hello-world-view';
import './views/main-layout';
export type ViewRoute = Route & {
 title?: string;
 icon?: string;
 children?: ViewRoute[];
};
export const views: ViewRoute[] = [
 // place routes below (more info https://vaadin.com/docs/latest/fusion/routing/overview)
 {
   path: '',
   component: 'hello-world-view',
   icon: '',
   title: '',
 },
 {
   path: 'hello',
   component: 'hello-world-view',
   icon: 'la la-globe',
   title: 'Hello World',
 },
 {
   path: 'about',
   component: 'about-view',
   icon: 'la la-file',
   title: 'About',
   action: async (_context, _command) => {
     await import('./views/about/about-view');
     return;
   },
 },
];
export const routes: ViewRoute[] = [
 {
   path: '',
   component: 'main-layout',
   children: [...views],
 },
];

The code in Listing 2 associates a path with a component. Like many JavaScript frameworks, Hilla uses a component to represent a view. In this case, when the user accesses the empty route, it serves the hello-world-view making up. (Note that other routes provide additional information like an icon, title, and action.)

The main layout is handled by /frontend/views/main-layout.tswhile the content of hello-world-view is in /frontend/views/helloworld/hello-world-view.tsshown in Listing 3.

Listing 3. hello-world-view.ts

import '@vaadin/button';
import '@vaadin/notification';
import { Notification } from '@vaadin/notification';
import '@vaadin/text-field';
import { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { View } from '../../views/view';
@customElement('hello-world-view')
export class HelloWorldView extends View {
 name="";
 connectedCallback() {
   super.connectedCallback();
   this.classList.add('flex', 'p-m', 'gap-m', 'items-end');
 }
 render() {
   return html`
     
     Say hello
   `;
 }
 nameChanged(e: CustomEvent) {
   this.name = e.detail.value;
 }
 sayHello() {
   Notification.show(`Hello ${this.name}`);
 }
}

The code in Listing 3 shows Lit constructing the view. If you’re familiar with responsive JavaScript idioms, the source should be pretty clear. Otherwise, see my recent introduction to Lit. the render() The method is responsible for outputting the contents of the view. We’ll use it here as a place to explore things. In particular, we want to see how to connect this front-end with our back-end Java endpoints.

Create the Java endpoints

Hilla is built on top of Spring Boot, so we can build the endpoints using Spring Web as usual. Hilla offers additional functionality to automatically generate the TypeScript that will be consumed in the Lit frontend.

Start by creating a new file in /src/java/main/com/example/application called MyEndpoint.java. Paste the contents of Listing 4 into this file.

Listing 4. MyEndpoint.java

package com.example.application;

import com.vaadin.flow.server.auth.AnonymousAllowed;
import dev.hilla.Endpoint;
import dev.hilla.Nonnull;

@Endpoint
@AnonymousAllowed
Public @Nonnull class MyEndpoint {
   public String foo() {
       return "bar";
   }
}

Hilla’s @Endpoint The annotation tells the framework that this class is a REST API. The class is also annotated with the @AnonymousAllowed annotation because Hilla secures all terminals via Spring security by default. the @Nonnull The annotation generates the appropriate type binding for the front-end TypeScript.

After saving this class file, you can observe that Hilla generated a new TypeScript file in /frontend/generated/MyEndpoint.ts. We will use this module to reach the endpoint from the view.

To note: Do not make any changes to these generated files; Hilla will overwrite them based on the changes made to the Java file.

Now go back to frontend/views/helloworld/hello-world-view.ts, where we’ll put our simple endpoint to work. In this case, we want to display the content of the call from the foo() full stop (which is “bar”). Listing 5 shows the additions you need to make to the hello-world-view.ts file. (Note that I removed most of the previous code and left only the additions for this list.)

Listing 5. Hello-world-view.ts

//...
import { customElement,property } from 'lit/decorators.js';
import { foo } from 'Frontend/generated/MyEndpoint';

@customElement('hello-world-view')
export class HelloWorldView extends View {
  //...
  @property()
  myString: string = "";
  constructor() {
    super();
    this.getString();
  }
  render() {
     return html`
     //...
     
${this.myString}
`; } async getString() { this.myString = await foo(); } }

The main point here is to import the foo() function of the MyEndpoint module and then use it to make a call against the remote back-end Java method we defined earlier.

To do this, we set a reactive property on the class with the Lit TypeScript annotation @propertywith the name string. We will use this property to store the server value. To fill it, we call the async getString() method, which simply calls the foo() function, and puts the return value in myString.

Hilla handles most of the work, including remote recovery, so we don’t have to think about it much.

Using Vaadin components in Hilla

As I noted earlier, Hilla is Vaadin Fusionso that apps built with Hilla can take advantage of all the well-designed components you might know from this framework. As an example, let’s use a Vaadin grid component to load a collection of novels with their titles and authors.

First, we’ll create a model object, which simply contains two Strings, as shown in Listing 6. This file is a typical Java data object. Save it as /src/main/java/com/example/application/Novel.java.

Listing 6. A model object for storing novels

package com.example.application;
import javax.validation.constraints.NotBlank;
public class Novel {
   @NotBlank
   private String title;
   @NotBlank
   private String author;
   public Novel(String title, String author){
     this.title = title;
     this.author = author;
   }
   public String getTitle() {
       return title;
   }
   public void setTitle(String title) {
       this.title = title;
   }
   public String getAuthor() {
       return author;
   }
   public void setAuthor(String author) {
       this.author = author;
   }
}

In Listing 7, we serve a List of novels by MyEndpoint.

Listing 7. MyEndpoint with a list of my favorite novels

package com.example.application;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import dev.hilla.Endpoint;
import dev.hilla.Nonnull;
@Endpoint
@AnonymousAllowed
public class MyEndpoint {
   private final List novels = new ArrayList();
   MyEndpoint(){
     Novel empireFalls = new Novel("Empire Falls", "Richard Russo");
     Novel reservationBlues = new Novel("Reservation Blues", "Sherman Alexie");
     Novel theAthenianMurders = new Novel("The Athenian Murders", "José Carlos Somoza");
     this.novels.add(empireFalls);
     this.novels.add(reservationBlues);
     this.novels.add(theAthenianMurders);
   }

   public @Nonnull List getNovels() {
     return this.novels;
   }
}

In Listing 7, we have prepared some novels with their authors and inserted them into the novels goods. We then exposed the data in the getNovels() period.

Now let’s display the new data, as shown in Listing 8. (Note that Listing 8 only shows the changed parts of the code.)

Listing 8. Using a grid to display novels

//...
import { foo, getNovels } from 'Frontend/generated/MyEndpoint';
import '@vaadin/grid/vaadin-grid';
@customElement('hello-world-view')
export class HelloWorldView extends View {
 @property()
 novels: object = {};
 constructor() {
   //...
   this.initNovels();
 }
 render() {
   return html`
     
       
       
     
   `;
 }
  async initNovels(){
     this.novels = await getNovels();
}

In this list we import the getNovels object of frontend/generated/MyEndpont, which Hilla generated for us. Then we use this method as the source for the content of this.novels.

Then we use this.novels to provide the .items imported property vaadin-grid making up. The end result is a well-formatted grid component with minimal effort.

Conclusion

This article introduced Hilla, a full-stack framework based on Vaadin Fusion. Hilla offers a well-integrated experience for building Java web applications with a responsive front-end. Thanks to Vaadin, it has many useful components ready to use. The examples in this article should give you an idea of ​​the collaboration with Hilla.

Copyright © 2022 IDG Communications, Inc.

Comments are closed.