Add button to tab in accordion

Hi,
Thanks for your excellent support on this forum - this is truly amazing. I would like to ask for some help yet another time.

I want to lazy load a tab within an accordion and then add a list of buttons dynamically to this tab. However, I am not able to actually add the button to the tab. Below is the code I have so far. Note that the last operation is not allowed (null pointer exception).


    @Inject
    private Accordion accExample;

    private ComponentsFactory componentsFactory;
    private boolean tabInitialised;

    @Override
    public void init(Map<String, Object> params) {
        accExample.addListener(new Accordion.TabChangeListener() {
            @Override
            public void tabChanged(Accordion.Tab newTab) {
                if ("lazyTab".equals(newTab.getName())) {
                    initLazyTab( newTab);
                }
            }
        });
    }

    private void initLazyTab(Accordion.Tab lazyTab) {
        if (tabInitialised) {
            return;
        }
        tabInitialised = true;
        Button newButton = componentsFactory.createComponent(Button.class);
        newButton.setCaption("New button");
        ((Container) lazyTab).add(newButton);
    }

With the related layout XML:


<accordion id="accExample">
                       <tab id="lazyTab" caption="Lazy Tab" lazy="true"/>
 </accordion>

I actually assumed that a tab is a container which would allow to add components but this somehow seems not to be the case.

Could you explain what I am missing here?

On a closer look it turns out that the null pointer exception comes from this line (not the last one):


Button newButton = componentsFactory.createComponent(Button.class);

Found out that I needed to declare the componentsFactory by @Inject. That solved the null pointer. But now I’m having this error:


ClassCastException: com.haulmont.cuba.web.gui.components.WebAccordion$Tab cannot be cast to com.haulmont.cuba.gui.components.Component$Container

There seems no way to add a button to a tab.

As a workaround (solution?), I am now adding a buttonpanel to the tab intially. However, I am not able to get access to this button panel as the tab (and its components) is not available before it is clicked and the tab interface does not provide access to the underlying buttonpanel.

I think I’m stuck. Please help.

As an alternative (yet another) I tried to dynamically add tabs on initialization. This works using the code below:


        Button dynButton = componentsFactory.createComponent(Button.class);
        dynButton.setCaption("Dynamic");
        ButtonsPanel dynPanel = componentsFactory.createComponent(ButtonsPanel.class);
        dynPanel.add(dynButton);
        accExample.addTab("Dynamic Tab", dynPanel);

But then the problem is that I cannot get the click event registered for the button. This can be done using the Vaadin native approach but such components cannot be added to the CUBA accordion component. So I’m stuck this way as well.

What am I missing here?

Hi Berend,

Add at least one container in your lazy tab:


<accordion id="accordion" height="100px" width="100%">
    <tab id="tab1" caption="Tab1"/>
    <tab id="tab2" caption="Tab2" lazy="true">
        <vbox id="tab2box"/>
    </tab>
</accordion>

When initializing the lazy tab in controller, use getComponentNN() method instead of injection:


public void init(Map<String, Object> params) {
    accordion.addListener(newTab -> {
        Label label = componentsFactory.createComponent(Label.class);
        label.setValue("Test");

        VBoxLayout tab2box = (VBoxLayout) getComponentNN("tab2box");
        tab2box.add(label);
    });
}

Hi Konstatin,
Thanks for your answer, have to try it out but it seems to be the solution for placing buttons in the accordion. I didn’t find the getComponentNN() anywhere so thanks for your help.

One thing remains a question to me. In what way can you set a method to invoke on click of the button that is dynamically added? I know how to do it with the native Vaadin button but that doesn’t mix with the Cuba components.

Could you answer this one as well? Thanks a lot!

In what way can you set a method to invoke on click of the button that is dynamically added?

Assign an action to the button:


Button helloBtn = componentsFactory.createComponent(Button.class);
helloBtn.setCaption("Say Hello");
helloBtn.setAction(new BaseAction("hello") {
    @Override
    public void actionPerform(Component component) {
        showNotification("Hello!", NotificationType.TRAY);
    }
});

See more details on actions here.

Hi Konstantin,
Again - excellent solution. I’m able to generate buttons on the fly now and have them trigger appropriate actions. Thanks a lot!