Creating a Main View

See the corresponding article for Fusion.

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

A main view created with the project builder

AppLayout is highly flexible and enables a range of main view layouts that can have either horizontal or vertical menus. These can be displayed statically or opened by clicking, and have many customizable elements. See the Design System documentation for a showcase of the features.

By default, the starter applications created with Vaadin Start use AppLayout to create the main view.

The Main View

A main view uses 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));
    }

You can customize the styling by specifying the path to the CSS files with the @CssImport annotation. The stylesheets should be placed below the frontend folder in the project. In a similar way, 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 on 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 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 to produce 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.

You can use a DrawerToggle component to toggle displaying the drawer.

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 displayed 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 both these things 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 retrieve 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 an AppLayout needs a route defined with the @Route annotation. The route needs to be linked to the main view by using the layout parameter to pass the class object of the main view.

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

Notice that the main view itself did not 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. You do this 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 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, as for the earlier route.

@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 displayed in AppLayout 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());
        });
    }

}