Refresh content in DetailsGenerator

Hi team,

The DataGrid uses DetailsGenerator to display expanded information of entities, if one of the entity property changed (e.g. use edit action), the expanded information is not updated.

Reproduce steps:

  1. clone project from GitHub - jmix-framework/jmix-petclinic: Example application built with Jmix v.1
  2. replace VeterinarianBrowse.java with below content:
package io.jmix.petclinic.screen.veterinarian.veterinarian;

import com.vaadin.ui.Grid;
import io.jmix.petclinic.entity.veterinarian.Veterinarian;
import io.jmix.ui.UiComponents;
import io.jmix.ui.action.BaseAction;
import io.jmix.ui.component.*;
import io.jmix.ui.model.CollectionLoader;
import io.jmix.ui.model.InstanceContainer;
import io.jmix.ui.navigation.Route;
import io.jmix.ui.screen.*;
import io.jmix.ui.screen.LookupComponent;
import io.jmix.ui.widget.JmixGrid;
import org.springframework.beans.factory.annotation.Autowired;

@UiController("petclinic_Veterinarian.browse")
@UiDescriptor("veterinarian-browse.xml")
@LookupComponent("veterinariansTable")
@Route(value = "veterinarians")
public class VeterinarianBrowse extends StandardLookup<Veterinarian> {
    @Autowired
    private DataGrid<Veterinarian> veterinariansTable;
    @Autowired
    private UiComponents uiComponents;
    @Autowired
    private CollectionLoader<Veterinarian> veterinariansDl;

    @Install(to = "veterinariansTable.detailGeneratorBtn", subject = "columnGenerator")
    private Component detailGeneratorBtnColumnGenerator(DataGrid.ColumnGeneratorEvent<Veterinarian> columnGeneratorEvent) {
        Veterinarian item = columnGeneratorEvent.getItem();
        Button button = uiComponents.create(Button.class);
        button.setCaption("...");
        button.addClickListener(e -> {
            veterinariansTable.setDetailsVisible(item, true);
        });
        return button;
    }

    @Install(to = "veterinariansTable", subject = "detailsGenerator")
    private Component tableDetailsGenerator(Veterinarian bean) {
        VBoxLayout mainLayout = uiComponents.create(VBoxLayout.class);
        mainLayout.setWidth("100%");
        mainLayout.setMargin(true);

        HBoxLayout headerBox = uiComponents.create(HBoxLayout.class);
        headerBox.setWidth("100%");

        Label<String> infoLabel = uiComponents.create(Label.TYPE_STRING);
        infoLabel.setHtmlEnabled(true);
        infoLabel.setStyleName("h1");
        infoLabel.setValue("Customer info:");

        Component closeButton = createCloseButton(bean);
        headerBox.add(infoLabel);
        headerBox.add(closeButton);
        headerBox.expand(infoLabel);

        Component content = getContent(bean);

        mainLayout.add(headerBox);
        mainLayout.add(content);
        mainLayout.expand(content);

        return mainLayout;
    }

    protected Component createCloseButton(Veterinarian entity) {
        Button closeButton = uiComponents.create(Button.class);
        closeButton.setIcon("font-icon:TIMES");
        BaseAction closeAction = new BaseAction("closeAction")
                .withHandler(actionPerformedEvent ->
                        veterinariansTable.setDetailsVisible(entity, false))
                .withCaption("");
        closeButton.setAction(closeAction);
        return closeButton;
    }

    protected Component getContent(Veterinarian entity) {
        Label<String> content = uiComponents.create(Label.TYPE_STRING);
        content.setHtmlEnabled(true);
        content.setId("contentLabel");

        StringBuilder sb = new StringBuilder();
        sb.append("<b>Full name</b><br>")
                .append(entity.getFirstName() + " " + entity.getLastName() + "<br><br>")
                .append("<b>Country</b><br>")
                .append("country" + "<br><br>")
                .append("<b>City</b><br>")
                .append("city");

        content.setValue(sb.toString());

        return content;
    }

    @Subscribe(id = "veterinariansDc", target = Target.DATA_CONTAINER)
    public void onVeterinariansDcItemChange(final InstanceContainer.ItemChangeEvent<Veterinarian> event) {
        // these method calls can not update details.
        veterinariansTable.setDetailsVisible(event.getItem(), false);
        veterinariansTable.setDetailsVisible(event.getItem(), true);
    }
}
  1. replace veterinarian-browse.xml with below content:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://jmix.io/schema/ui/window"
        xmlns:c="http://jmix.io/schema/ui/jpql-condition"
        caption="msg://veterinarianBrowse.caption"
        focusComponent="veterinariansTable">
    <data readOnly="true">
        <collection id="veterinariansDc"
                    class="io.jmix.petclinic.entity.veterinarian.Veterinarian">
            <fetchPlan extends="_base"/>
            <loader id="veterinariansDl">
                <query>
                    <![CDATA[select e from petclinic_Veterinarian e]]>
                </query>
            </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
        <screenSettings id="settingsFacet" auto="true"/>
    </facets>
    <actions>
        <action id="lookupSelectAction"
                caption="msg:///actions.Select"
                icon="LOOKUP_OK"
                primary="true"
                shortcut="${COMMIT_SHORTCUT}"/>
        <action id="lookupCancelAction"
                caption="msg:///actions.Cancel"
                icon="LOOKUP_CANCEL"/>
    </actions>
    <dialogMode height="600"
                width="800"/>
    <layout expand="veterinariansTable" spacing="true" >
        <filter id="filter"
                dataLoader="veterinariansDl">
            <properties include=".*"/>
        </filter>
        <dataGrid id="veterinariansTable"
                  width="100%"
                  dataContainer="veterinariansDc">
            <actions>
                <action id="create" type="create"/>
                <action id="edit" type="edit"/>
                <action id="remove" type="remove"/>
            </actions>
            <columns>
                <column property="firstName" id="firstName"/>
                <column property="lastName" id="lastName"/>
                <column id="detailGeneratorBtn">
                    <componentRenderer/>
                </column>
            </columns>
            <simplePagination/>
            <buttonsPanel id="buttonsPanel"
                          alwaysVisible="true">
                <button id="createBtn" action="veterinariansTable.create"/>
                <button id="editBtn" action="veterinariansTable.edit"/>
                <button id="removeBtn" action="veterinariansTable.remove" stylename="danger"/>
            </buttonsPanel>
        </dataGrid>
        <hbox id="lookupActions" spacing="true" visible="false">
            <button action="lookupSelectAction"/>
            <button action="lookupCancelAction"/>
        </hbox>
    </layout>
</window>
  1. start the app, open Master Data → Veterinarians from the main menu.
  2. expand details of one entity by click the newly added button in the row.
  3. edit the entity’s last name property and save the entity.

ER: the expanded details should show the updated last name of the entity.
AR: nothing changed in the expanded view.

Hello Bryan!

It’s not strictly a bug; Vaadin does not re-render item details if they are already open. This is probably done for performance reasons. You should bind your Label component to the item to automatically update its content.

Thanks @pinyazhin ! We just tried to bind a label to property as you suggested, it works.