Hello,
In the code snippet below (Vaadin 24), we need to include the option to close a tab and we also need to select a tab programmatically, but we found code examples on how to do this.
Can you help us?
TabSheet ts = new TabSheet();
VerticalLayout l1 = new VerticalLayout();
l1.add(new H1("First Tab"));
Tab tab1 = new Tab(l1);
VerticalLayout l2 = new VerticalLayout();
l2.add(new H1("Second Tab"));
Tab tab2 = new Tab(l2);
ts.add("Fisrt", tab1);
ts.add("Second", tab2);
ts.setSelectedTab(tab2); // does not work
There is a misunderstanding - you don’t have to wrap the Tab’s content in a Tab. Using add(String, Component) is meant to be used with your vertical layout directly. The add call also returns the created tab to be stored / used in other methods (like select)
I was able to implement the tab close button successfully. This works fine with the Tabs component. Extend Vaadin’s Tab class. Create a wrapper layout and add (an icon,) the caption and the close button.
About the JS event listener with e.stopPropagation():
This prevents that the Tab triggers a click event when the close button is clicked. When the tab is also clicked, the tab is selected by the TabSheet and when you got a Grid within the tab layout the data would be loaded. But we want the tab to be closed and do not need to open the tab within this roundtrip.
Here is the code:
public class V8Tab extends Tab {
public Span caption = new Span();
private String captionText = null;
public Component icon = null;
public Button closeButton = new Button(VaadinIcon.CLOSE_SMALL.create());
private Div wrapper = new Div();
private boolean closable = false;
private String key;
private String iconAltText;
private String url;
public V8Tab(String key, String caption, Component icon) {
this.key = key;
setCaption(caption);
setIcon(icon);
build();
}
private void build() {
closeButton.setVisible(isClosable());
closeButton.getElement().setAttribute("part", "v8-tab-close");
closeButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY_INLINE,
ButtonVariant.LUMO_CONTRAST);
closeButton.addClickListener(remove -> {
fireEvent(new TabCloseEvent(this, remove.isFromClient()));
});
closeButton.getElement().executeJs("this.addEventListener('click', e => e.stopPropagation())");
if (icon != null) {
wrapper.add(icon);
}
caption.setClassName("v8-tab-caption");
wrapper.add(caption, closeButton);
wrapper.setClassName("v8-tab-wrapper");
add(wrapper);
}
public Registration addTabCloseListener(ComponentEventListener<TabCloseEvent> listener) {
return addListener(TabCloseEvent.class, listener);
}
public boolean isClosable() {
return closable;
}
public void setClosable(boolean closable) {
this.closable = closable;
setCloseButton();
}
private void setCloseButton() {
closeButton.setVisible(isClosable());
}
public boolean isEnabled() {
return super.isEnabled();
}
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
}
public void setCaption(String text) {
captionText = text;
this.caption.setText(text);
}
public String getCaption() {
return captionText;
}
public Component getIcon() {
return icon;
}
public void setIcon(Component icon) {
removeIcon();
if (icon != null) {
this.icon = icon;
addIcon();
}
}
private void addIcon() {
if (icon != null) {
icon.getElement().setAttribute("part", "v8-tab-icon");
wrapper.addComponentAtIndex(0, this.icon);
}
}
public void removeIcon() {
if (this.icon != null) {
wrapper.remove(this.icon);
icon.getElement().removeAttribute("part");
}
this.icon = null;
}
public void setIcon(Component icon, String iconAltText) {
setIcon(icon);
setIconAlternateText(iconAltText);
}
public String getIconAlternateText() {
return iconAltText;
}
public void setIconAlternateText(String iconAltText) {
this.iconAltText = iconAltText;
}
public static class TabCloseEvent extends ComponentEvent<V8Tab> {
private static final long serialVersionUID = 3268493040165192509L;
public TabCloseEvent(V8Tab source, boolean fromClient) {
super(source, fromClient);
}
}
Here how to add:
TabSheet ts = new TabSheet();
V8Tab tab1 = new V8Tab("somekey", "First", null);
tab1.addCloseListener(cEvent -> ts.remove(tab1));
ts.add(tab1, yourLayout);
plusButton.addClickListener(event -> {
final TabIconCreator tabIconCreator = new TabIconCreator(tabSheet);
tabSheet.add(tabIconCreator.createTabIcon(), this.createContentLayout());
tabSheet.setSelectedTab(tabSheet.getTabAt(tabSheet.getSelectedIndex() + 1));
});
This part allows you to close the tab
closeSpanCross.addClickListener(event -> {
var confirm = ConfirmDialogBuilder.showConfirmInformation("Do you want to delete this tab ?");
confirm.addConfirmListener(confirmEvent -> {
int selectedTab = tabSheet.getSelectedIndex();
tabSheet.remove(selectedTab);
});
});
I am using Vaadin version 24 and I have implemented it a little differently, as shown in the code below. After removing the tab, the processing icon stays on continuously and does not stop.
The same behavior occurs when I use the suggested V8Tab implementation.
if (forms.containsKey(formClass.getName())) {
tabs.setSelectedTab(tabs.getTab(forms.get(formClass.getName())));
} else {
clazz = classLoader.loadClass(formClass.getName());
BMFormBase form = (BMFormBase) clazz.getDeclaredConstructor().newInstance();
tabs.add(“”, form);
Button closeButton = new Button(VaadinIcon.CLOSE_SMALL.create());
closeButton.setVisible(true);
closeButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY_INLINE,
ButtonVariant.LUMO_CONTRAST);
closeButton.addClickListener(remove → {
tabs.remove(tabs.getTab(form));
forms.remove(formClass.getName());
});
closeButton.getElement().executeJs(“this.addEventListener(‘click’, e => e.stopPropagation())”);
caption.setText(form.getTituloTab());
wrapper.add(caption, closeButton);
tabs.getTab(form).add(wrapper);
tabs.setSelectedTab(tabs.getTab(form));
forms.put(formClass.getName(), form);
}