Creating a Main View

See the corresponding article for Fusion.

The AppLayout component allows creating typical main views that have a header, a menu, and a content area. It fully works with routing and navigation, making it easier to set them up than wiring them by yourself. It is responsive to changes in the screen size and orientation, and adapts both to desktop and mobile browsers.

A main view created with the project builder

AppLayout is highly flexible, allowing many kinds of main view layouts, either with horizontal or vertical menus, which can be displayed statically or open by clicking, and having many customizable elements. See the Design System documentation for a showcase of the features.

The starter applications created with the Vaadin project builder by default use the AppLayout to create the main view.

The Main View

A main view uses the AppLayout by extending it and setting up the elements in the constructor. You need to set the content for the header and the drawer. You can have an application menu either horizontally in the header (navigation bar) or vertically in the drawer.

The following example shows how to create a main view with a vertical menu in the drawer, and a button to open and close it in the navigation bar:

@CssImport("./styles/views/main/main-view.css")
@JsModule("./styles/shared-styles.js")
public class MainView extends AppLayout {
    private final Tabs menu;
    private H1 viewTitle;

    public MainView() {
        // Use the drawer for the menu
        setPrimarySection(Section.DRAWER);

        // Make the nav bar a header
        addToNavbar(true, createHeaderContent());

        // Put the menu in the drawer
        menu = createMenu();
        addToDrawer(createDrawerContent(menu));
    }

Creating the header and the menu content is described below.

You can customize the styling by giving path to CSS files with the @CssImport annotation. The stylesheets need to be put under the frontend folder in the project. Likewise you can include JavaScript code with the JsModule] annotation. In the example we include a JavaScript file that sets shared custom styles. See Loading Resources for more details about importing the CSS and JavaScript resources.

Notice that the main view itself does not have a route, as it is only a frame for the actual content views.

Creating a Header

The navigation bar of the AppLayout is a horizontal element that can contain any component, such as a header or a horizontal menu. Applications created with the project builder use it for a header containing a menu toggle, a view title, and a user image.

A header with drawer (menu) toggle, view title, and a user avatar.

Whether the drawer is displayed can be toggled with a DrawerToggle component.

The following example creates such a header, using the viewTitle member in the main view class:

private Component createHeaderContent() {
    HorizontalLayout layout = new HorizontalLayout();

    // Configure styling for the header
    layout.setId("header");
    layout.getThemeList().set("dark", true);
    layout.setWidthFull();
    layout.setSpacing(false);
    layout.setAlignItems(FlexComponent.Alignment.CENTER);

    // Have the drawer toggle button on the left
    layout.add(new DrawerToggle());

    // Placeholder for the title of the current view.
    // The title will be set after navigation.
    viewTitle = new H1();
    layout.add(viewTitle);

    // A user icon
    layout.add(new Image("images/user.svg", "Avatar"));

    return layout;
}

Creating a Menu

A menu can be displayed either in the navigation bar or in the drawer. In an application created by the project builder, it is in the drawer as described here.

private Component createDrawerContent(Tabs menu) {
    VerticalLayout layout = new VerticalLayout();

    // Configure styling for the drawer
    layout.setSizeFull();
    layout.setPadding(false);
    layout.setSpacing(false);
    layout.getThemeList().set("spacing-s", true);
    layout.setAlignItems(FlexComponent.Alignment.STRETCH);

    // Have a drawer header with an application logo
    HorizontalLayout logoLayout = new HorizontalLayout();
    logoLayout.setId("logo");
    logoLayout.setAlignItems(FlexComponent.Alignment.CENTER);
    logoLayout.add(new Image("images/logo.png", "My Project logo"));
    logoLayout.add(new H1("My Project"));

    // Display the logo and the menu in the drawer
    layout.add(logoLayout, menu);
    return layout;
}

The actual menu is a vertical Tabs component. It is filled from a list of Tab components. Each tab contains a RouterLink to the corresponding view.

private Tabs createMenu() {
    final Tabs tabs = new Tabs();
    tabs.setOrientation(Tabs.Orientation.VERTICAL);
    tabs.addThemeVariants(TabsVariant.LUMO_MINIMAL);
    tabs.setId("tabs");
    tabs.add(createMenuItems());
    return tabs;
}

private Component[] createMenuItems() {
    return new Tab[]{
        createTab("Hello World", HelloWorldView.class),
        createTab("Card List", CardListView.class),
        createTab("About", AboutView.class)};
}

private static Tab createTab(String text, Class<? extends Component> navigationTarget) {
    final Tab tab = new Tab();
    tab.add(new RouterLink(text, navigationTarget));
    ComponentUtil.setData(tab, Class.class, navigationTarget);
    return tab;
}

Handling Navigation

When the user navigates to a view, the tab for the view should be highlighted by setting it as selected. You can also set the view title in header.

You can do these tasks by overriding afterNavigation() in AppLayout as follows:

@Override
protected void afterNavigation() {
    super.afterNavigation();

    // Select the tab corresponding to currently shown view
    getTabForComponent(getContent()).ifPresent(menu::setSelectedTab);

    // Set the view title in the header
    viewTitle.setText(getCurrentPageTitle());
}

The selected tab should correspond to the displayed content (the view).

You can find it as follows:

private Optional<Tab> getTabForComponent(Component component) {
    return menu.getChildren().filter(tab -> ComponentUtil.getData(tab, Class.class).equals(component.getClass()))
            .findFirst().map(Tab.class::cast);
}

You can get the view title from the PageTitle annotation given to the view (see Creating a View).

private String getCurrentPageTitle() {
    return getContent().getClass().getAnnotation(PageTitle.class).value();
}

The same title is automatically set as the page title in the browser, so you do not need to handle that.

Creating a View

Like any view, a view displayed in a AppLayout needs a route defined with the @Route annotation. The route needs to be linked to the main view, by giving the class object of the main view with the layout parameter.

@Route(value = "hello", layout = MainView.class)

Notice that the main view itself didn’t have a route. To have an entry point to the main view, and often to the entire application, you need to define a default view using a route alias, as described in Default View.

Page Title

You can set the page title shown in the browser window or tab using the @PageTitle annotation.

@PageTitle("Hello World")

You can use the page title in a view header or breadcrumbs, as shown in Handling Navigation.

Styling a View

You can define custom styling for the view with the @CssImport annotation.

@CssImport("./styles/views/helloworld/hello-world-view.css")

Default View

Most applications need an entry point. You can define a view as the default view by defining the route of the main view for it with @RouteAlias. The value defining the route needs to be empty for the root route.

You also need to define the enclosing main view with the layout parameter, like for the route earlier.

@RouteAlias(value = "", layout = MainView.class)

You could also have /main for the main view and then /main/hello for the sub-view.

Finishing the View

Otherwise a view is like any other view or a composite component.

public class HelloWorldView extends HorizontalLayout {
    private TextField name;
    private Button sayHello;

    public HelloWorldView() {
        setId("hello-world-view");
        name = new TextField("Your name");
        sayHello = new Button("Say hello");
        add(name, sayHello);
        setVerticalComponentAlignment(Alignment.END, name, sayHello);

        // Handle clicks
        sayHello.addClickListener(e -> {
            Notification.show("Hello " + name.getValue());
        });
    }

}