I have a grid with a ComponentRenderer setup like this. My goal is to show all details of all items in the grid by default.
grid.setItems(dataProvider);
// this prevents other items from collapsing when one is clicked (being expanded)
grid.setDetailsVisibleOnClick(false);
// collapse/expand on click
grid.addItemClickListener(event -> grid.setDetailsVisible(event.getItem(), !grid.isDetailsVisible(event.getItem())));
grid.addComponentColumn((item) -> {
HorizontalLayout layout = new HorizontalLayout();
// ... configure and populate layout (omitted)
// expand all details by default
// doing this somehow causes the itemClickListener to fail... ("Child should have this element as a parent")
if (!grid.isDetailsVisible(item)) {
grid.setDetailsVisible(item, true);
}
return layout;
});
grid.setItemDetailsRenderer(new ComponentRenderer<>(() -> {
VerticalLayout layout = new VerticalLayout();
layout.setPadding(false);
layout.setMargin(false);
return layout;
}, (layout, item) -> {
List<Details> details = detailService.listDetails(this.userId, item);
populateDetails(layout, details);
}));
I achieved this by using setItemDetailsVisible
in addComponentColumn
, however, this causes the ItemClickListener to fail with an exception, as the comments in the code suggest.
Why does this happen? How can it be fixed? Is there perhaps a different approach to this problem which works out of the box?
I am using Vaadin 23.3.5.
This is the StackTrace for the exception.
java.lang.IllegalArgumentException: Child should have this element as a parent
at com.vaadin.flow.dom.Node.ensureChildHasParent(Node.java:589)
at com.vaadin.flow.dom.Node.removeChild(Node.java:482)
at com.vaadin.flow.dom.Node.removeChild(Node.java:466)
at com.vaadin.flow.data.provider.AbstractComponentDataGenerator.refreshData(AbstractComponentDataGenerator.java:53)
at com.vaadin.flow.data.provider.CompositeDataGenerator.lambda$refreshData$2(CompositeDataGenerator.java:62)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at com.vaadin.flow.data.provider.CompositeDataGenerator.refreshData(CompositeDataGenerator.java:62)
at com.vaadin.flow.data.provider.DataCommunicator.refresh(DataCommunicator.java:391)
at com.vaadin.flow.component.grid.Grid$AbstractGridExtension.refresh(Grid.java:1136)
at com.vaadin.flow.component.grid.Grid$DetailsManager.setDetailsVisible(Grid.java:1204)
at com.vaadin.flow.component.grid.Grid.setDetailsVisible(Grid.java:3143)
at my.comp.project.gui.MyView.lambda$initGrid$9b1b5227$1(MyView.java:190)
Line 190 is the ItemClickListener
Since I could not find an appropriate solution to this issue, I decided to opt for a workaround.
Instead of using setItemDetailsRenderer
I just render an instance of Details
inside of a component column.
grid.addComponentColumn(item -> {
HorizontalLayout header = new HorizontalLayout();
// configure and populate
VerticalLayout content = new VerticalLayout();
// configure and populate
Details details = new Details(header, content);
details.addClassName("full-width");
details.setOpened(true);
return details;
});
In order to have the details summary span across the whole column width, I added the class name summary-width-full
and added this style to vaadin-details.css
:host(.full-width) [part="summary-content"] {
width: 100%;
}