Application Basics

In this article, we give basics of application development with Vaadin Fusion.

The entry point of a Fusion application is the application shell, which consists of the following files:

frontend/index.html

A bootstrap page that loads the application. You do not normally need to modify the file.

frontend/index.ts

The TypeScript entry point of the application. This is where you set up routing and other application tasks.

Prerequisites

Fusion applications have certain prerequisites.

To write the bootstrapping code for an application or create views in TypeScript, make sure that:

  • Client-side bootstrapping is enabled.

  • The index.html page template and the index.ts entry point exist in the frontend/ folder.

If you are starting with a starter application, they should already have been taken care of.

After that, any .ts file imported from index.ts (statically or dynamically) is going to be built as a part of the application.

Starting the Application

Vaadin Fusion starts the application using a bootstrap template (index.html). The entry point to the application is normally written in TypeScript, but can as well be or JavaScript.

In the following, is an example of the index.ts and index.html files. It adds a button click listener to load the full application lazily, after the button is clicked.

import { Router } from '@vaadin/router';
import { Flow } from '@vaadin/flow-frontend';

const {serverSideRoutes} = new Flow({
  imports: () => import('../target/frontend/generated-flow-imports')
});

const loadButton: HTMLElement | null = document.getElementById('loadButton');
if (loadButton) {
  loadButton.addEventListener('click', async() => {
    const router = new Router(document.querySelector('#outlet'));
    await router.setRoutes(serverSideRoutes);
  });
}

With the above snippets, the bootstrap page shows "Hello, World!" text with a button. The Vaadin client engine is not loaded until the button is clicked.

See the Starting the application in TypeScript page for details on starting a Vaadin application from TypeScript code.

Configuring TypeScript Compiler

TypeScript compiler requires a tsconfig.json file, vaadin-maven-plugin generates one in case there is no tsconfig.json in the project root. The default configuration looks similar to the following:

{
  "compilerOptions": {
    "sourceMap": true,
    "inlineSources": true,
    "module": "esNext",
    "target": "es2017",
    "moduleResolution": "node",
    "strict": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitReturns": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "experimentalDecorators": true,
    "baseUrl": "frontend",
    "paths": {
      "Frontend/*": [
        "*"
      ]
    }
  },
  "include": [
    "frontend/**/*.ts",
    "frontend/index.js",
    "types.d.ts"
  ],
  "exclude": []
}

Adding a TypeScript View

To navigate between views, you need to define routes (see Defining Routes for more details). First, create a client-side view:

import { css, html, LitElement, render } from 'lit';
import { customElement, query } from 'lit/decorators.js';
import { guard } from 'lit/directives/guard.js';

import '@vaadin/vaadin-button/vaadin-button';
import '@vaadin/vaadin-notification/vaadin-notification';

@customElement('my-view')
export class MyView extends LitElement {
  @query('#notification')
  private notification: any;

  static get styles() {
    return css`
      :host {
        display: block;
      }
    `;
  }

  render() {
    return html`
      <vaadin-button theme="primary" @click="${this.clickHandler}">
        Click me
      </vaadin-button>
      <vaadin-notification
        id="notification"
        duration="2000"
        .renderer="${guard([], () => (root: HTMLElement) => {
          render(
            html`Hello, World!`,
            root
          );
        })}"
      ></vaadin-notification>
    `;
  }

  private clickHandler() {
    this.notification.open();
  }
}

In order to see the new client-side view in the browser, you need to define a new client-side route for it. That requires the following changes in the index.html and index.ts files:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>TypeScript Support in Vaadin</title>
  <!-- index.ts is included here automatically (either by the dev server or during the build) -->
</head>
<body>
  <script src="VAADIN/build/webcomponentsjs/webcomponents-loader.js"></script>
  <div id="outlet"></div>
</body>
</html>
import { Flow } from '@vaadin/flow-frontend';
import { Router } from '@vaadin/router';

import './my-view';

const {serverSideRoutes} = new Flow({
  // @ts-ignore
  imports: () => import('../target/frontend/generated-flow-imports')
});

const routes = [
    {path: '', component: 'my-view'},
    ...serverSideRoutes
];

export const router = new Router(document.querySelector('#outlet'));
router.setRoutes(routes);

Now my-view is accessible via the root path, that is http://localhost:8080/. All the other routes are handled by the server-side router. See Defining Routes for more information.

Hot Reload in Development Mode

When running the application in development mode, all modifications in frontend folder are compiled automatically. Refreshing the browser is enough to see the updates in the application.

Note
Server restart is required
When adding index.ts or index.html, the application server needs to be restarted to update the entry point and the bootstrap template.

Accessing Backend Data in TypeScript Views

Fusion provides a type-safe and secured way to access data from backend in frontend views using the generated TypeScript code. Vaadin scans the backend code during development and generates TypeScript code which can call the corresponding Java methods. The generated code is processed through the same way as other TypeScript views. Only the necessary code is included in the production application bundle. See Accessing backend from TypeScript for more information.