Vaadin Grid show all item details by default

270 views Asked by At

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

2

There are 2 answers

0
paresi On BEST ANSWER

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%;
}
1
ollitietavainen On

Speculation: This sounds like a problem with the order of execution. You're trying to expand the Grid row for item while the ComponentRenderer is executing. I'm guessing the row for item (or the Details for said row) hasn't been attached to the parent Grid yet, so trying to expand the Details at this point is executing too early.