Skip to content
This repository has been archived by the owner on May 6, 2021. It is now read-only.

Latest commit

 

History

History
298 lines (229 loc) · 11.1 KB

creating-routes.asciidoc

File metadata and controls

298 lines (229 loc) · 11.1 KB
title order layout
Creating Routes in TypeScript
40
page

Creating Routes in TypeScript

Client-side routing allows combining client-side views created in TypeScript with server-side views created in Java using the @Route annotation.

This is one of the building blocks that enable navigating between views when offline because it does not require a server connection for navigation between client-side views.

Warning
This feature is experimental and it will change before the next Long-Term-Supported Vaadin version. If you have an idea how to make it more useful for you, please share it on GitHub.

Client-side routing is based on using Vaadin Router and starting the app in TypeScript. Client-side routes are explicitly listed in the routing config, and server-side routes are added there through the serverSideRoutes Flow TypeScript API.

The following snippet—​explained further on this page—​shows how to combine client and server views so that the server-side is invoked when no client-side routes match.

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

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

export const router = new Router(document.querySelector('#outlet'));
router.setRoutes([
  {
    path: 'categories',
    component: 'app-categories',
    action: async () => { await import('./views/app-categories-view'); }
  },
  // for server-side, the next magic line sends all unmatched routes:
  ...serverSideRoutes // IMPORTANT: this must be the last entry in the array
]);

The @vaadin/router module facilitates navigation when client and server views are combined in the same application, as well as when common layouts are used in the page.

The glue for mixing views is the @vaadin/flow-frontend/Flow module which allows lazy initialization of Flow and navigation to server views.

Vaadin Flow Client

Flow provides a client module which acts as a bridge between client router and server routes.

Even though it is possible to integrate Flow with any JavaScript based routing solution.

The Flow client is distributed as a part of the com.vaadin:vaadin Maven artifact. It is automatically included into any Vaadin application and can be imported into any TypeScript file as import {Flow} from '@vaadin/flow-frontend/Flow';.

Usage

First, you have to import the module, and then you have to create the Flow instance.

At this point, it is needed to specify the location to the Flow generated file with the imports for Java views, typically /target/frontend/generated-flow-imports.js in a Vaadin maven project.

Notice that the import() function should be used in order to lazy load Flow dependencies the first time the user navigates to a server-side view.

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

Finally, make Vaadin Router pass all unmatched paths to Flow server by adding …​serverSideRoutes at the end of the router configuration block:

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

export const router = new Router(document.querySelector('#outlet'));
const {serverSideRoutes} = new Flow({
  imports: () => import('../target/frontend/generated-flow-imports')
});

router.setRoutes([
  {
    path: 'categories',
    component: 'app-categories',
    action: async () => { await import('./views/app-categories-view'); }
  },
  // for server-side, the next magic line sends all unmatched routes:
  ...serverSideRoutes // IMPORTANT: this must be the last entry in the array
]);

Vaadin Router

Vaadin Router is a small yet powerful client-side router JavaScript library. It uses a widely adopted express.js syntax for routers (users/:id) to map URLs to views. It has all the features of a modern router: async route resolution, animated transition, child routes, navigation guards, redirects, and more.

Vaadin router works with Web Components regardless of how they are created, it also offers a JavaScript API for regular HTML elements.

Vaadin Router is distributed as the @vaadin/vaadin-router npm package. It is included into @vaadin/vaadin-core, and is automatically included into any Vaadin application.

Usage

Add Vaadin Router into the index.ts file by using the import statement, then create and export a router instance by passing the outlet element in the index.html file:

index.html
<html>
  <body>
    <h1>My App</h1>
    <div id="outlet"></div>
  </body>
</html>
index.ts
import {Router} from '@vaadin/router';
const outlet = document.querySelector('#outlet');
export const router = new Router(outlet);

Router configuration is done by using a set of routes that map URL paths to components.

Vaadin Router goes through the routes until the first match is found, then it creates an element instance for the component specified by the route, and inserts this element in the router outlet, replacing any pre-existing elements.

router.setRoutes([
  {
    path: 'help',
    component: 'app-help',
    action: async () => { await import('./views/app-help-view'); }
  },
  {
    path: 'categories',
    component: 'app-categories',
    action: async () => { await import('./views/app-categories-view'); }
  }
]);

The router instance can be imported and used in the component’s file:

app-help.ts
import {customElement, html, LitElement, property} from 'lit-element';
import {router} from './index';

@customElement('app-help')
class AppHelpElement extends LitElement {
  @property({type: Object}) location = router.location;

  render() {
    return html`
      <p>Your location URL: ${this.location.getUrl()}</p>
      <nav>
        <a href="${router.urlForPath('/categories')}">Categories</a>
      </nav>
    `;
  }
}

Layouts

When using client-side routing it is possible to compose the page layout without any server intervention, so as the initial application page also known as Application Shell can be shown when in offline mode.

Vaadin Router allows to group related routes together under a common parent by using the children property during the configuration:

router.setRoutes([
  {
    path: '',
    component: 'app-layout',
    action: async () => { await import('./views/app-layout-view'); },
    children: [
      {
        path: 'help',
        component: 'app-help',
        action: async () => { await import('./views/app-help-view'); }
      },
      {
        path: 'categories',
        component: 'app-categories',
        action: async () => { await import('./views/app-categories-view'); }
      }
    ]
  }
]);

Router Navigation Events

The router executes callbacks on each view to check if the navigation must continue, be postponed or redirected. The way to implement navigation controllers differs depending on whether the view is on the client or server side.

Vaadin Router navigation lifecycle (client-side views)

When returning an element or Web Component in a client view, developer might implement the following lifecycle interfaces:

  • BeforeEnterObserver: onBeforeEnter(location, commands, router) callback is executed before the outlet container is updated with the new element. At this point, user can cancel the navigation.

  • AfterEnterObserver: onAfterEnter(location, commands, router) callback is executed after the new element has been attached to the outlet. he difference between this method and onBeforeEnter is that when this method is executed, there is no way to abort the navigation.

  • BeforeLeaveObserver: onBeforeLeave(location, commands, router) callback is executed before the previous element is going to be detached. Navigation can be cancelled at this point.

  • AfterLeaveObserver: onAfterLeave(location, commands, router) callback is executed before the element is going to be removed from the DOM. When this method is executed, there is no way to abort the navigation.

During the execution of onBeforeEnter and onBeforeLeave callbacks, user might postpone navigation by returning commands.prevent(), but only in onBeforeEnter, navigation can be redirected by returning commands.redirect(path).

Note
Lifecycle callbacks are asynchronous.

The following snippets show how to cancel navigation in a Web Component:

my-demo.ts
import {customElement, LitElement} from 'lit-element';
import {
  BeforeEnterObserver,
  PreventAndRedirectCommands,
  Router,
  RouterLocation
} from '@vaadin/router';

@customElement('my-view')
class MyView extends LitElement implements BeforeEnterObserver {
  onBeforeEnter(
      location: RouterLocation,
      commands: PreventAndRedirectCommands,
      router: Router) {
    if (location.pathname === '/cancel') {
      return commands.prevent();
    }
  }
}
index.ts
import {Router} from '@vaadin/router';

...

export const router = new Router(document.querySelector('#outlet'));
router.setRoutes([
  {
    path: 'view1',
    component: 'my-view',
    action: async () => { await import('./views/my-view'); }
  }
]);

For more information visit Vaadin Router API documentation.

Flow Router navigation lifecycle (server-side views)

For server-side views routing events are handled as described in the Vaadin Navigation Lifecycle chapter.

The way to interact with lifecycle events in Java is by implementing the following interfaces:

  • Any attached Components implementing BeforeEnterObserver will receive an event before a new navigation state is entered. There is the possibility to reroute or forward to another navigation target.

  • Attached components implementing BeforeLeaveObserver will receive an event before leaving the current navigation state. Navigation can be postponed, or rerouted or forwarded to another target.

  • Components implementing AfterNavigationObserver will receive an event after all navigation tasks have resolved.

In the following example, navigation is cancelled when the view is dirty.

public class MyView extends Div implements BeforeLeaveObserver {
    @Override
    public void beforeLeave(BeforeLeaveEvent event) {
        if (this.isDirty()) {
            event.postpone();
        }
    }

    private boolean isDirty() {
        return true;
    }
}
Note
Rerouting from server to client side is not supported yet.