Persisting Navigation Menu between CDIViews

Does anyone know how to persist a Navigation Menu when navigating between CDIViews?

For example, after login you go to a CDIView called HomeView, which has a navigation tree panel on the left and a URI Fragment of #!home. You then open up the navigation tree and go in a few level and click an item. This navigates to a URI Fragment of #!reports and opens a new view which as I understand refreshes the entire screen, including the navigation menu. This has two issues (1) you’re redrawing the entire Tree/Navigation Menu, (2) You’re losing the state of the Tree, it’ll be entirely closed again.

How can the navigation menu be persisted in this case?

You do not need to refresh the entire page. You can define which ComponentContainer you want your navigator to put the view in when you navigate, so you can keep the navigation visible at all times.

[code]
NavTree navTree = new NavTree();//your navigation tree component
CssLayout viewContainer = new CssLayout();
HorizontalLayout rootLayout = new HorizontalLayout(navTree, viewContainer);

Navigator nav = new Navigator(this, viewContainer);//first parameter is your UI
[/code]With this setup, your views will always be shown in the viewContainer CSSLayout instead of replacing your entire UI contents.

Thanks Marcus,

This looks promising. One thing I noticed is that the navigateTo takes over the entire fragment, even if it’s only altering a part of a page. So though I started with #home, and most of the page is #home, when I navigate the subcontainer to the ReportsView, the URI fragment becomes #reports. For bookmarking this is problematic, because #reports doesn’t have the menu.

Possibly I should be doing URI fragments in the form of #!main and #!main/reports, but if I get the parameter in the enter method and call navigateTo in there, I’m still calling navigateTo and updating the hashbang. I tried overriding Navigator methods to make it do what I want, but it turned into a bit of a hornet’s nest and I found that when you create a navigator, it automatically turns that navigator into the main UI navigator, which isn’t what I wanted and seems like it could cause problems later(I’m a bit worried about creating any new navigators once I saw that).

Do you have any suggestions on controlling the hashbang?

Well, I found one solution, though I’d like to think there’s a way to do it without manually correcting the URIFragment, but here’s what I have:

@CDIView("maintest")
public class MainTestView extends CustomComponent implements Serializable, View {
    
    @Inject
    private CDIViewProvider viewProvider;
    private CssLayout viewContainer;
    
    @Override
    public void enter(ViewChangeEvent event) {
        Navigator nav = new Navigator(UI.getCurrent(), viewContainer);
        nav.addProvider(viewProvider);
        
        String parameter = event.getParameters();
        if (StringUtils.isNotEmpty(parameter))
        {
            nav.navigateTo(parameter);
            UI.getCurrent().getPage().setUriFragment("!maintest/"+parameter, false);
        }
    }
    
    public MainTestView() {
    }
    
    @PostConstruct
    public void postContruct()
    {
        viewContainer = new CssLayout();

        Label label1 = new Label();
        label1.setValue("test1");
        label1.addContextClickListener(new ContextClickListener() {
            @Override
            public void contextClick(ContextClickEvent event) {
                Navigator nav = new Navigator(UI.getCurrent(), UI.getCurrent());
                nav.addProvider(viewProvider);
                nav.navigateTo("maintest/testone");
            }
        });;

        Label label2 = new Label();
        label2.setValue("test2");
        label2.addContextClickListener(new ContextClickListener() {
            @Override
            public void contextClick(ContextClickEvent event) {
                Navigator nav = new Navigator(UI.getCurrent(), UI.getCurrent());
                nav.addProvider(viewProvider);
                nav.navigateTo("maintest/testtwo");
            }
        });;

        HorizontalLayout rootLayout = new HorizontalLayout(label1, label2, viewContainer);
        setCompositionRoot(rootLayout);
    }
}


@CDIView("testone")
public class Test1View extends CustomComponent implements Serializable, View {

    @Override
    public void enter(ViewChangeEvent event) {
    }
    
    @PostConstruct
    public void postContruct()
    {
        Label label = new Label();
        label.setValue("This is TestView 1");
        setCompositionRoot(label);
    }
}

// Test2View is same with different message and URI Fragment Path

Take Two.

The above works but it failed the main qualification of not recreateing the nav. Appearently navigateTo and setUriFragment both recreate the whole view. The below is much simpler and doesn’t have that problem, while still allowing proper bookmarking.

@SuppressWarnings("serial") 
@CDIView("maintest")
public class MainTestView extends CustomComponent implements Serializable, View {
    
    @Inject
    private CDIViewProvider viewProvider;
    private CssLayout viewContainer;
    
    @Override
    public void enter(ViewChangeEvent event) {
        
        String parameter = event.getParameters();
        if (StringUtils.isNotEmpty(parameter))
        {
            setBody(parameter);
        }
        
    }

    private void setBody(String parameter) {
        Navigator nav = new Navigator(UI.getCurrent(), viewContainer);
        nav.addProvider(viewProvider);

        nav.navigateTo(parameter);
        UI.getCurrent().getPage().setUriFragment("!maintest/"+parameter, false);

        nav = new Navigator(UI.getCurrent(), UI.getCurrent());
        nav.addProvider(viewProvider);
    }
    
    public MainTestView() {
    }
    
    @PostConstruct
    public void postContruct()
    {
        viewContainer = new CssLayout();

        Label label1 = new Label();
        label1.setValue("test1");
        label1.addContextClickListener(new ContextClickListener() {
            @Override
            public void contextClick(ContextClickEvent event) {
                setBody("testone");
            }
        });

        Label label2 = new Label();
        label2.setValue("test2");
        label2.addContextClickListener(new ContextClickListener() {
            @Override
            public void contextClick(ContextClickEvent event) {
                setBody("testtwo");
            }
        });;

        HorizontalLayout rootLayout = new HorizontalLayout(label1, label2, viewContainer);
        setCompositionRoot(rootLayout);
    }
}