Navigate to the previous view

Hi guys, I am using Vaadin 8, and I would implement the back button.

I have the button:

Button back= new Button(I18NHelper.i18n("ui.back")); back.addStyleName(ValoTheme.BUTTON_DANGER); back.addClickListener(e -> UI.getCurrent().getNavigator().navigateTo(DefaultView.VIEW_NAME)); This button back to the DefaultView, but I would send the user to the previous View.

I found a middle solution, overriding method enter, as the follow:

@Override
public void enter(ViewChangeEvent event) {
    oldView = event.getOldView();
}

oldView variable is global in the class.

Now I would like to do, something like that:

back.addClickListener(e -> UI.getCurrent().getNavigator().navigateTo(oldView));

but there is no possibility to do it with my last code row.

Please help me!

Thanks

Hi Fabio,

Currently there is no implementation for navigateTo that can navigate to a View :-/
However, here is some workaround:

  1. Create a middle interface:

public interface NamedView extends View { String getViewName(); } 2. Make your views implement that interface instead of View.
3. Implement the new getViewName method togive unique identifier for views.
3. The enter method will be:

@Override public void enter(ViewChangeEvent event) { oldView = (NamedView) event.getOldView(); } 4. Finally, you can navigate using:

UI.getCurrent().getNavigator().navigateTo(oldView.getViewName())
A^2

Hi,

Yes, just add an abstract getViewName() method in SecuredView and then in each subclass implement the getViewName() method. In there you would just return a String that is the name of the view. For example:

public String getViewName() { return "my-view"; } BR,
Goran

Thanks for help.
I already have something like:

public abstract class SecuredView extends VerticalLayout implements View {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    @Autowired
    private AccountService accountService;

    @Autowired
    private MessageNotificationService notificationService;

    @Override
    public void enter(ViewChangeEvent event) {

        Account account = accountService.getConnectedAccount();
        ViewPermission[] viewPermission = getClass().getAnnotationsByType(ViewPermission.class);
        if (viewPermission.length > 0) {

            Permission[] permissions = viewPermission[0]
.value();
            for (Permission permission : permissions) {

                try {
                    account.assertCan(permission);
                }
                catch (MissingPermission mp) {

                    notificationService.showNotification(I18NHelper.i18n("ui.lackPermission") + " " + permission.name());
                    UI.getCurrent().getNavigator().navigateTo(DefaultView.VIEW_NAME);
                }

            }
        }

    }
}

and all my view extend SecuredView.

Maybe I can put getViewName in SecuredView, but I donìt understand what I have to do in getViewName method

Perfect it works, but I have the last issue.

In my

public class Application extends UI implements ViewDisplay

I create the menu, with button back. I manage the previous page in this way:

oldViewName = (oldViewName == null) ? DefaultView.VIEW_NAME : oldViewName;

currentViewName = navigator.getState();

leftMenu.addComponent(new MenuLayout(accountService, NavigationMenu.BACK, oldViewName));
oldViewName = currentViewName;

MenuLayout class, build the menu, creating the buttons, and associating on the press button the navigation to oldViewName.

Doing this, it works, but in my opinion, it’s very bad. There is some other approach?
Thanks

Hi Fabio,

Great if it works! Why do you beleive your approach is bad?
You could post more code examples or give a github link. That way we could maybe point out some improvements.

BR,
Goran

Ok, I try to add my class:

@SpringUI
@SpringViewDisplay
public class Application extends UI implements ViewDisplay {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    @Autowired
    private SpringNavigator navigator;

    @Autowired
    private AccountService accountService;

    private Panel springViewDisplay;

    private String oldViewName;

    private String currentViewName;

    private List<String> list;

    @Override
    protected void init(VaadinRequest request) {

        navigator.init(this, (ViewDisplay) this);

        list = buildListViewBack();
    }

    @Override
    public void showView(View view) {

        MHorizontalLayout hl = new MHorizontalLayout();
        hl.withResponsive(true).withSpacing(false).withFullHeight().withFullWidth();

        springViewDisplay = new Panel();
        springViewDisplay.setSizeFull();

        // calculate oldViewName
        // if oldViewName is null, it means that it's the first time that I open the web app
        // and since the first page (View) is managed by DefaultView class,
        // I set oldViewName =  DefaultView.VIEW_NAME
        oldViewName = (oldViewName == null) ? DefaultView.VIEW_NAME : oldViewName;

        currentViewName = navigator.getState();

        if (list.contains(currentViewName)) {
            hl.addComponent(new MenuLayout(NavigationMenu.BACK, oldViewName));
        } else {
            hl.addComponent(new MenuLayout(NavigationMenu.NAVIGATION, null));

            setContent(hl);
        }

        oldViewName = currentViewName;

        hl.addComponent(springViewDisplay);
        hl.setExpandRatio(springViewDisplay, 1.0f);

        setContent(hl);

        springViewDisplay.setContent((Component) view);
    }

    @WebListener
    public static class MyContextLoaderListener extends ContextLoaderListener {
    }

    @Configuration
    @EnableVaadin
    public static class MyConfiguration {

        @Bean
        @UIScope
        public SpringNavigator navigator() {
            return new SpringNavigator();
        }

    }

    @WebServlet(urlPatterns = { "/ui/*", "/VAADIN/*" }, name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = Application.class, productionMode = false)
    public static class MyUIServlet extends SpringVaadinServlet {
    }

    // I have the list of the view which must have the back menu
    // maybe it can be made in a different way, I don't know
    private List<String> buildListViewBack() {

        List<String> listView = new ArrayList<>();

        listView.add(SupplierSignUp.VIEW_NAME);
        listView.add(CompanySignUp.VIEW_NAME);
        listView.add(CompanyAccountSignUp.VIEW_NAME);
        listView.add(SupplierAccountSignUp.VIEW_NAME);
        listView.add(SupplierDetail.VIEW_NAME);
        listView.add(EditCompanyRequiredDocument.VIEW_NAME);
        listView.add(EditCompanyDocument.VIEW_NAME);
        listView.add(EditProduct.VIEW_NAME);
        listView.add(AddProduct.VIEW_NAME);
        listView.add(EditAccount.VIEW_NAME);

        return listView;
    }
}

and here my MenuLayout, which manage also the left menu in my web app

public class MenuLayout extends CustomComponent {

    @Autowired
    private AccountService accountService;

    private MCssLayout menuLayout;

    private Account account;

    public MenuLayout(NavigationMenu navigationMenu, String string) {
        setCompositionRoot(init(navigationMenu, string));
        setWidth("200px");
        setHeight("100%");
    }

    public MCssLayout init(NavigationMenu navigationMenu, String buildBackMenu) {

        account = accountService.getConnectedAccount();

        menuLayout = new MCssLayout();
        ......
        
        // NavigationMenu is my custom enum 
        /* public enum NavigationMenu { NAVIGATION, BACK } */

        if (NavigationMenu.NAVIGATION.equals(navigationMenu)) {
            buildNavigationMenu();
        } else {
            buildBackMenu(buildBackMenu);
        }

        return menuLayout;
    }

    private Button createNavigationButton(String caption, final String viewName, VaadinIcons icon) {
        Button button = new Button(caption);
        button.setIcon(icon);
        button.addClickListener(event -> getUI().getNavigator().navigateTo(viewName));
        return button;
    }

    private void buildNavigationMenu() {

        Label headerUserMenu = new Label("Caption");
        menuLayout.add(headerUserMenu);
     
        ......
       
        if (account.can(Permission.permission1))
           menuLayout.addComponent(createNavigationButton("Caption", myView.VIEW_NAME, VaadinIcons.DOCTOR));
      
       ......

    }

    private void buildBackMenu(String oldViewName) {

        menuLayout.addComponent(createNavigationButton("Back", oldViewName, VaadinIcons.ARROW_BACKWARD));

    }
    
}

That’s all

The code looks good!
I can suggest a slight improvment when it comes to navigation, instead of re-creating the menuLayout everytime you navigate. Maybe you can fix the menu and receive events during naviagtion to flip between “back button” and “navigation button”. In the event you can also send the oldViewName as a param.

Your navigator initialisation can be changed from:

navigator.init(this, this); To:

navigator.init(this, springViewDisplay); Hope this helps :slight_smile:


A^2

Good suggestion!
But I am not sure, it fits with my needs.
That’s why, my menu could change in every moment.

I try to exaplain.

There is an admin view and a user view.

If I logged in as user, and for example I am not activated yet (by admin), I cannot see, for example a particular menu (let’s call it, uploadDocuments).

At this stage, maybe, meanwhile I navigate in the web site, the admin activate me, and I have to see the uploadDocuments menu. But if I do like you suggested me, if I’m right, I will not see the menu.

Right?

Hi Fabio,

The
View interface
has an
enter method
, which receives a
ViewChangeEvent
. This event has some properties including the oldView.

Maybe you could store the oldView in a UI scoped bean and just update this bean once a view’s enter method is executed.

Regards!

If the menu is very dynamic and complicated then recreating it might be the way to go, but according to your code, the “user” will not see the “uploadDocuments menu” till she navigates to a new view, because the menu will be rebuilt only after navigation. Isn’t it? Please correct me if I’m wrong.

You can consider two solutions:

A) Divide your UI into two parts: “menuArea” and “contentArea”, navigation should happen all the time on the “contentArea”, and during each navigation, you send an event to the “menuArea” to be updated (to show more menus / hide buttons …etc).

B) Same structure as “A”, but even better, update the “menuArea” with
push
events for immediate actions. That way, the user will see the new menu immediately regardless of navigation.

Hope this give some ideas :slight_smile:


A^2

Great!
Thanks for all guys!