0

I am still new to Vaadin so, please bear with it.

We are currently migrating from Vaadin framework 8.0 to 8.3.2. One of the reasons of doing is that there's a requirement of using tree for the menu. Since 8.0 doesn't have tree, the workaround for generating a menu is by instantiating an inner Button class with the help of an Enum class in a loop (for user permission control):

public final class ValoMenuItemButton extends Button {

        private static final String STYLE_SELECTED = "selected";

        private final DashboardViewType view;

        public ValoMenuItemButton(final DashboardViewType view) {
            this.view = view;
            setPrimaryStyleName("valo-menu-item");
            setIcon(view.getIcon());
            setCaption(view.getViewName().substring(0, 1).toUpperCase()
                    + view.getViewName().substring(1));
            DashboardEventBus.register(this);
            addClickListener(new ClickListener() {
                @Override
                public void buttonClick(final ClickEvent event) {
                    UI.getCurrent().getNavigator()
                            .navigateTo(view.getViewName());
                }
            });

        }

        @Subscribe
        public void postViewChange(final PostViewChangeEvent event) {
            removeStyleName(STYLE_SELECTED);
            if (event.getView() == view) {
                addStyleName(STYLE_SELECTED);
            }
        }
    }

The enum class structure is built in this manner:

AUDIT("Receipt Validation", RcptValidation.class, FontAwesome.BAR_CHART_O, false),
        AUDIT1("Matching - Receipt not in SYCARDA", RcptNotInSycarda.class, FontAwesome.BAR_CHART_O, false), 
        AUDIT2("Matching - Receipt not in POS", RcptNotInPos.class, FontAwesome.BAR_CHART_O, false), 
        AUDIT3("Missing Sequence", MissSequence.class, FontAwesome.BAR_CHART_O, false),
        AUDIT4("*Debug Purposes", LineAmtVsTotal.class, FontAwesome.BAR_CHART_O, false);
  
   
    private DashboardViewType(final String viewName,
            final Class<? extends View> viewClass, final Resource icon,
            final boolean stateful) {
        this.viewName = viewName;
        this.viewClass = viewClass;
        this.icon = icon;
        this.stateful = stateful;
    }

So far, I've not found any examples that are written around the v8 framework, while most of the sample code that I've seen are based on v7 framework.

I've attempted to write something like this, but the tree sub menus do not come out as it is (I've left out the expand and collapse event as that can be handled later).

My attempted code on the tree is this:

TreeData <String> treeData = new TreeData(); 
treeData.addRootItems("Dashboard","Sales","Sales Pattern","Top SKUs","Audit"); 
// The loop starts here (for DashboardViewType view: DashboardViewType.values)
        if(enabled){ 
        if(StringUtils.startsWith(view.getViewName(), "SALES")){ 
            if (StringUtils.contains(view.getViewName(),"SALES_PATTERN")){ 
                treeData.addItem( "Sales Pattern", view.getViewName()); 
            }else{ treeData.addItem( "Sales", view.getViewName());
 } 
        }else if (StringUtils.startsWith(view.getViewName(), "TOP_SKUS")){ 
            treeData.addItem( "Top SKUs", view.getViewName()); 
        }else if (StringUtils.startsWith(view.getViewName(), "AUDIT")){ 
            treeData.addItem( "Audit", view.getViewName()); 
        }else if (StringUtils.startsWith(view.getViewName(), "DASHBOARD")){ 
            treeData.addItem( "Dashboard", view.getViewName()); 
        } 
        DashboardEventBus.register(view);
            } 
// loop ends here

    Tree<String> tree = new Tree<>("Sycarda Dashboard"); 
    tree.setDataProvider(new TreeDataProvider<>(treeData)); 
    tree.setItemIconGenerator(item -> { return FontAwesome.BAR_CHART_O; }); 
    tree.expand("Sales","Sales Pattern","Top SKUs","Audit"); 
    tree.addSelectionListener(e -> new Button.ClickListener() { 
        @Override public void buttonClick(Button.ClickEvent event) { 
            DashboardEventBus.register(event); 
            UI.getCurrent().getNavigator().navigateTo(event.getClass().getName()); 
        } 
    });

This was posted originally at the Vaadin forum, but since there were no answers to that, I am putting it here. I'd appreciate if there's any input or another approach for this problem.

Thanks in advance.

Melvin Mah
  • 105
  • 2
  • 13
  • I might be missing something here, but at least in the Enum you provided, none of the views start with "SALES", "TOP_SKUS" etc? Edit: I see the code is a bit differnet in the Vaadin.com forum, I'll get back to you after reading it – Erik Lumme Apr 16 '18 at 08:34
  • Still not entirely sure how you define which ones are children and which ones are root elements? – Erik Lumme Apr 16 '18 at 08:40

2 Answers2

2

In Vaadin 8 you can simply define the "get children" method when adding the data. In your case the enum class should provide some method like "getSubItems", which you could then set as the value provider. The following example shows it in a similar way, where "rootItems" is simply the same as your top level enum instances and MenuItem the same as your enumeration.

    static {
        rootItems = Arrays.asList(...);
    }

    @PostConstruct
    private void init() {
        Tree<MenuItem> tree = new Tree<>();

        tree.setItems(rootItems, MenuItem::getSubItems);
    }

    private class MenuItem {
        private String name;
        private Resource icon;
        private Collection<MenuItem> subItems;

        public Collection<MenuItem> getSubItems() {
            return subItems;
        }

        // ... other getter and constructor omitted;
    }
Stefan
  • 21
  • 1
0

Someone has shown an example and it is similar to what Stefan mentioned. In context with my requirement, the steps involved include:

  1. Create a wrapper class that includes:

    private DashboardViewType view;
    private Resource icon;
    private boolean stateful;
    private Class<? extends View> viewClass;
    private String viewName;
    
    //Create the get / set methods for those attributes above
    //Constructor for the wrapper class is below. 
    
    public TreeMenuItem(DashboardViewType view){
        this.view = view;
    }
    
  2. For the Enum class additional main menu items are added. Default main class can be used since you can't put a null.

    public enum DashboardViewType {

    SALES("Sales",DashboardView.class,FontAwesome.HOME,false),
    SALES_PATTERN("Sales Pattern",DashboardView.class,FontAwesome.HOME,false),
    TOP_SKUs("Top SKUs",DashboardView.class,FontAwesome.HOME,false),
    AUDIT("Audit",DashboardView.class,FontAwesome.HOME,false)
    

    }

  3. The tree is built in this manner:

    private Component buildTree(){

    Tree<TreeMenuItem> tree = new Tree<>();
    TreeData<TreeMenuItem> treeData = new TreeData<>();
    
    
    //This is for items that have no child.                
    TreeMenuItem dashboardItem = new TreeMenuItem(DashboardViewType.DASHBOARD);
    dashboardItem.setIcon(VaadinIcons.HOME_O);
    dashboardItem.setStateful(DashboardViewType.DASHBOARD.isStateful());
    dashboardItem.setViewName(DashboardViewType.DASHBOARD.getViewName());
    treeData.addItem(null, dashboardItem);
    
    for (DashboardViewType type : DashboardViewType.values()) {
       TreeMenuItem menuItem = new TreeMenuItem(type);
       menuItem.setIcon(VaadinIcons.HOME_O);
       menuItem.setViewName(type.getViewName());
       menuItem.setStateful(false);
       treeData.addItem(null, menuItem);
       getSubMenuItems(type).forEach(subView -> {
           TreeMenuItem subItem = new TreeMenuItem(subView);
           subItem.setViewName(subView.getViewName().substring(0, 1).toUpperCase()
                                            + subView.getViewName().substring(1));
           subItem.setIcon(subView.getIcon());
           subItem.setStateful(subView.isStateful());
           subItem.setView(subView);
           subItem.setViewClass(subView.getViewClass());
           treeData.addItem(menuItem, subItem);
        }); 
        }
     }
    tree.setDataProvider(new TreeDataProvider<>(treeData));
    tree.setItemIconGenerator(TreeMenuItem::getIcon);
    tree.setItemCaptionGenerator(TreeMenuItem::getViewName);
    tree.addItemClickListener((Tree.ItemClick<TreeMenuItem> event) -> {
                DashboardEventBus.register(event.getItem().getView());           UI.getCurrent().getNavigator().navigateTo(event.getItem().getViewName());
            });
    

    }

  4. The logic to create subviews:

    private List getSubMenuItems(DashboardViewType type) {

        List<DashboardViewType> dashboardList;
        switch(type){
            case TOP_SKUs:
            dashboardList =  new LinkedList<>(Arrays.asList(DashboardViewType.TOP_SKUs1,
                                 DashboardViewType.TOP_SKUs2,
                                 DashboardViewType.TOP_SKUs3,
                                 DashboardViewType.TOP_SKUs4));
    
            filterByUserLevel(dashboardList,subACL4);
            return dashboardList;
    
            case AUDIT:
            dashboardList =  new LinkedList<>(Arrays.asList(DashboardViewType.AUDIT1,
                                 DashboardViewType.AUDIT2,
                                 DashboardViewType.AUDIT3,
                                 DashboardViewType.AUDIT4,
                                 DashboardViewType.AUDIT5));
    
            filterByUserLevel(dashboardList,subACL5);
            return dashboardList;    
    
            case DASHBOARD:
                break;
            default:
                break;
        }
    
        return Collections.emptyList();
    }
    

Add additional cases if required so. After that, the function controls remove the elements that are not part of the user level:

private List<DashboardType> filterByUserLevel(List<DashboardType>list, String u){

    if(list.size() == subACL.length()){
       for(int i=0; i<list.size(); i++){
       if(StringUtils.substring(subACL, i, i+1).equalsIgnoreCase("0")){
          list.remove(i);
        }
      }
     Collections.sort(list);
     return list;
     //this removes unwanted sub-menu items according current user level.
    }

}
Melvin Mah
  • 105
  • 2
  • 13