0

I want to reach the following: A dialog contains a layout with a tab bar, that in turn contains the component containers for each tab.

private final Map<Tab, Component> tabComponentMap = new LinkedHashMap<>();
public DialogLayout()
    {
        super();
        this.initUI();

        final Tabs tabs             = this.createTabs();
        final Div  contentContainer = new Div();
        this.addComponentAtIndex(1, tabs);
        this.addComponentAtIndex(2, contentContainer);

        tabs.addSelectedChangeListener(e -> {
            contentContainer.removeAll();
            contentContainer.add(this.tabComponentMap.get(e.getSelectedTab()));
        });

        contentContainer.add(this.tabComponentMap.get(tabs.getSelectedTab()));
    }
private Tabs createTabs()
    {
        this.tabComponentMap.put(new Tab("1"), new View1());
        this.tabComponentMap.put(new Tab("2"), new View2());
        this.tabComponentMap.put(new Tab("3"), new View3());
        return new Tabs(this.tabComponentMap.keySet().toArray(new Tab[]{}));
    }

When i open the dialog Tab 1 should be open (that's default). There are two button in each View (back/next) that effect the navigation between the tabs. So if I'm in Tab1 with View1 and click next, Tab2 with View2 in it opens and so on. If possible the dialog should remain intact so only the tabs change. How do i accomplish this goal and what do the button click events in each View need to contain? I don't know how to access the tabs in the DialogLayout from the respective View.

  • I could hand over tabs and all the other tab objects in `new View1(tabs, ...)` to the View1 class, but is there another possibilty? –  Oct 22 '19 at 08:41
  • @BasilBourque ignore the slider function. I accidently mixed two ideas. I will change the post immediately –  Oct 22 '19 at 08:42
  • By the way, you might like the [`paged-tabs`](https://vaadin.com/directory/component/paged-tabs) component that wraps the `Tabs` widget bundled with Vaadin. – Basil Bourque Oct 22 '19 at 17:15
  • It seems that what you may be trying to do is walk the user through a series of progressive steps in collecting choices towards a goal, with ability to go back and change some of their previous choices. This kind of functionality is known as a [*Wizard*](https://en.wikipedia.org/wiki/Wizard_(software)), a user-interface idiom invented by a technical writer at Microsoft many years ago. Horizontal tabs are understood by the user for arbitrary switching, without the notion of progressing towards a goal. Use vertical tabs with "Continue"/"Back" buttons in lower right corner that move tab to tab. – Basil Bourque Oct 23 '19 at 21:21

1 Answers1

1

Would something like this work?

@Route("dialogs-tabs")
public class DialogContainerTabs extends VerticalLayout {
    public DialogContainerTabs(){
        Dialog dialog=new Dialog();

        Tab tab1 = new Tab("Tab one");
        Div page1 = new Div();
        page1.setText("Page#1");

        Tab tab2 = new Tab("Tab two");
        Div page2 = new Div();
        page2.setText("Page#2");
        page2.setVisible(false);

        Tab tab3 = new Tab("Tab three");
        Div page3 = new Div();
        page3.setText("Page#3");
        page3.setVisible(false);

        Map<Integer, Component> tabsToPages = new HashMap<>();
        tabsToPages.put(0, page1);
        tabsToPages.put(1, page2);
        tabsToPages.put(2, page3);
        Tabs tabs = new Tabs(tab1, tab2, tab3);
        Div pages = new Div(page1, page2, page3);
        Set<Component> pagesShown = Stream.of(page1)
                .collect(Collectors.toSet());

        tabs.addSelectedChangeListener(event -> {
            pagesShown.forEach(page -> page.setVisible(false));
            pagesShown.clear();
            Component selectedPage = tabsToPages.get(tabs.getSelectedIndex());
            selectedPage.setVisible(true);
            pagesShown.add(selectedPage);
        });

        Button back=new Button();
        back.setIcon(VaadinIcon.ARROW_BACKWARD.create());
        dialog.add(back);
        back.addClickListener(e->{
           if(tabs.getSelectedIndex()>0){
               pagesShown.forEach(page -> page.setVisible(false));
               pagesShown.clear();
               tabs.setSelectedIndex(tabs.getSelectedIndex()-1);
               Component selectedPage = tabsToPages.get(tabs.getSelectedIndex());
               selectedPage.setVisible(true);
               pagesShown.add(selectedPage);
           }
           else{
               //Do what you want, maybe navigate to the last one? or NOOP
           }
        });


        Button forward=new Button();
        forward.setIcon(VaadinIcon.ARROW_FORWARD.create());
        dialog.add(forward);
        forward.addClickListener(e->{
            if(tabs.getSelectedIndex()<tabsToPages.size()-1){
                pagesShown.forEach(page -> page.setVisible(false));
                pagesShown.clear();
                tabs.setSelectedIndex(tabs.getSelectedIndex()+1);
                Component selectedPage = tabsToPages.get(tabs.getSelectedIndex());
                selectedPage.setVisible(true);
                pagesShown.add(selectedPage);
            }
            else{
                //Do what you want, maybe navigate to the first one one? or NOOP
            }
        });


        dialog.add(tabs);
        dialog.add(pages);
        dialog.setOpened(true);
        add(dialog);
    }

}

I have buttons on top of tabs, but you could put them inside the view, if you want so, of course. In Vaadin 14, tabs doesn't hold components, so once you have switched a tab, you should manually set previous component to hidden and the new one to visible

The result dialog looks like this: enter image description here

And tabs code is taken directly from here : Tabs : Java Examples

P.S. It's just a POC and this might not be exactly 1:1 with what you want, but you could get an idea from here, how to follow :) And this code should be refactored to not re-use the same logic three times (in tabs listener and back/forward buttons)

anasmi
  • 2,562
  • 1
  • 13
  • 25
  • I'm completely stuck and totally confused. I tried to change my approach in order to get the same result, but i messed it up. What exactly do i have to do in my code to only enable the selected tab and to navigate via button clicks? My map works so far, so the tabs are linked to the correct content. –  Oct 22 '19 at 11:45
  • In your button listeners, you should add the same code as inside the `addSelectedChangeListener`. My map has ``, where integer represents an index of a Tab and content is a content displayed on tab click. So in case "back" is pressed, I am navigting to a (tab -1) via `setSelectedIndex`. – anasmi Oct 22 '19 at 11:49
  • change `tabComponentMap ` to be `Map` than you could instead of Tab add as identifier its position and then in button listeners easily get a content, as you can get a currently selected tab via `tabs.getSelectedIndex()` – anasmi Oct 22 '19 at 11:50
  • @nidaav I would change it as it's the easiest way to find next/previous tab. Otherwise, you need to figure out a way, how to get a previous/next Tab from the map. And this will require additional work https://stackoverflow.com/questions/30099237/how-to-get-the-previous-key-value-and-the-next-key-value-in-maps – anasmi Oct 22 '19 at 11:59
  • Thank you! Is it possible to navigate only with the buttons, not with the tabs themselves? –  Oct 22 '19 at 12:59
  • The easiest would be to set `tabs.setEnabled(false)`, but then you would loose all blue styles around tabs. Otherwise, you could try to set a `pointer-events: none;` to tabs, but you would need to find a correct css to apply it. – anasmi Oct 22 '19 at 13:15