vaadinvaadin-flowvaadin-grid

Vaadin Grid show all item details by default


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


Solution

  • 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%;
    }