Skip to content

Vaadin 8 | Dynamic Initalization

appreciated edited this page Jun 25, 2018 · 1 revision

Dynamic Submenus

On the first glance it might look like the AppLayout cannot be configured with dynamic content.
In fact it is actually possible to do so. If you don't build the AppLayout right away but instead keep the instance of the AppLayoutBuilder (But remember to build it before adding it to the UI / Layout). This allows you to call the add method dynamically from other places.
Note: The same applies to the SubmenuBuilder.

@Override
protected void init(VaadinRequest vaadinRequest) {
    // Build there lists any way (f.e. dynamically) you want
    List<MenuItem> myDynamicSubmenu1 = Arrays.asList(
            new MenuItem("View2", VaadinIcons.ABACUS, View2.class),
            new MenuItem("View3", VaadinIcons.ABACUS, View3.class)
    );
    List<MenuItem> myDynamicSubmenu2 = Arrays.asList(
            new MenuItem("View4", VaadinIcons.ABACUS, View4.class),
            new MenuItem("View5", VaadinIcons.ABACUS, View5.class)
    );

    AppLayoutBuilder builder = AppLayoutBuilder.get(Behaviour.LEFT_RESPONSIVE_HYBRID)
            .withTitle("My Title")
            .withDesign(AppBarDesign.MATERIAL)
            .withDefaultNavigationView(MyDefaultView.class)
            .add(new MenuHeader("My Title", new ThemeResource("icon.png")))
            .add("View1", VaadinIcons.ABACUS, View1.class);

    SubmenuBuilder submenu1 = SubmenuBuilder.get("Submenu1", VaadinIcons.FILE_TREE);
    myDynamicSubmenu1.forEach(item -> submenu1.add(item.name, item.icon, item.viewClass));
    builder.add(submenu1.build());

    SubmenuBuilder submenu2 = SubmenuBuilder.get("Submenu2", VaadinIcons.FILE_TREE);
    myDynamicSubmenu1.forEach(item -> submenu1.add(item.name, item.icon, item.viewClass));
    builder.add(submenu2.build());

    setContent(builder.build());
}

class MenuItem {
    String name;
    Resource icon;
    Class<View> viewClass;

    MenuItem(String name, Resource icon, Class<View> viewClass) {
        this.name = name;
        this.icon = icon;
        this.viewClass = viewClass;
    }
}

Note: Replace MenuItem with your own entity.

Translating a tree like structure into menu elements

You might have the problem that you already have a menu structure but don't know how to translate it into this menu. Here is an example how it can be done:

@Override
protected void init(VaadinRequest vaadinRequest) {
    // Build this tree any way (f.e. dynamically) you want
    List<MenuItem> items = Arrays.asList(
            new MenuItem("View1", VaadinIcons.ABACUS, View1.class),
            new MenuItem("Submenu", VaadinIcons.FILE_TREE, Arrays.asList(
                    new MenuItem("SubmenuItem 1", VaadinIcons.ACADEMY_CAP, View2.class),
                    new MenuItem("SubmenuItem 2", VaadinIcons.ACADEMY_CAP, View3.class),
                    new MenuItem("SubmenuItem 3", VaadinIcons.ACADEMY_CAP, View4.class)
            )),
            new MenuItem("View1", VaadinIcons.ABACUS, View5.class)
    );
    AppLayoutBuilder builder = AppLayoutBuilder.get(Behaviour.LEFT_RESPONSIVE_HYBRID)
            .withTitle("My Title")
            .withDesign(AppBarDesign.MATERIAL)
            .withDefaultNavigationView(MyDefaultView.class)
            .add(new MenuHeader("My Title", new ThemeResource("icon.png")));
    items.forEach(menuItem -> {
                if (menuItem.hasSubItems()) {
                    SubmenuBuilder submenuBuilder = SubmenuBuilder.get(menuItem.name, menuItem.icon);
                    menuItem.subItems.forEach(item -> addSubMenuItem(submenuBuilder, item));
                    builder.add(submenuBuilder.build());
                } else {
                    builder.add(menuItem.name, menuItem.icon, menuItem.viewClass);
                }
            }
    );
    setContent(builder.build());
}

private void addSubMenuItem(SubmenuBuilder builder, MenuItem item) {
    if (item.hasSubItems()) { // if the item still has children -> recursion
        SubmenuBuilder submenuBuilder = SubmenuBuilder.get(item.name, item.icon);
        item.subItems.forEach(subItem -> addSubMenuItem(submenuBuilder, subItem));
        builder.add(submenuBuilder.build());
    } else {
        builder.add(item.name, item.icon, item.viewClass);
    }
}

// incomplete `Composite pattern` which is used to represent your menu element tree
class MenuItem {
    String name;
    Resource icon;
    Class<View> viewClass;

    List<MenuItem> subItems = null;

    MenuItem(String name, Resource icon, Class<View> viewClass) {
        this(name, icon, (List<MenuItem>) null);
        this.viewClass = viewClass;
    }

    MenuItem(String name, Resource icon, List<MenuItem> subItems) {
        this.name = name;
        this.icon = icon;
        this.subItems = subItems;
    }

    boolean hasSubItems() {
        return subItems != null;
    }
}

Note: Replace MenuItem with your own entity.