Очистка списка связанных сущностей в treeDataGrid

Доброго времени.

Jmix 2.3.3

При построчном редактировании записи в treeDataGrid не получается очистить список связанных сущностей. В классе сущности поле является List и помечено @Composition и @OneToMany. При создании записи в detail-view все сохраняется. А вот при редактировании строки в list-view при нажатии на крестик визуально список очищается, но при сохранении он опять виден. При логировании также видно, что список не обнуляется и его элементы остаются на месте. Остальные поля, которые не являются списками, очищаются и сохраняются корректно.

image

И вообще как при построчном редактировании кастомизировать выбор/очистку значений? На списке же нет никаких entityLookup/entityClear в отличие от detail-view.

При удалении в лукапе выбранного в списке значения и подтверждении в строке тоже якобы происходит очистка, но на самом деле список связанных сущностей не меняется.
image

Здравствуйте

Дело в том, что автоматическая генерация компонентов для DataGridEditor определяет атрибут сущности как коллекцию, поэтому для его создаётся поле для редактирования в виде MultiValuePicker компонента, к которому добавлено два действия: MultiValueSelectAction и ValueClearAction.

Если обратиться к реализации этих действий, то можно увидеть, что они не передают изменения в контекст данных, а просто визуально изменяют компонент.

Я завёл задачу на то, чтобы переработать механизм генерации в случае композитной сущности: DefaultGenerationStrategy generates MultiValuePicker for entity composition attribute · Issue #3708 · jmix-framework/jmix · GitHub

Чтобы это исправить, достаточно определить другой компонент для редактирования. Это можно сделать в контроллере экрана:

    @ViewComponent
    private DataGrid<Order> ordersDataGrid;

    @Autowired
    private UiComponents uiComponents;
    @Autowired
    private Actions actions;

    @Subscribe
    public void onInit(final InitEvent event) {
        ordersDataGrid.getEditor()
                .setColumnEditorComponent("orderItems", context -> {
                    JmixMultiSelectComboBoxPicker<OrderItem> picker =
                            uiComponents.create(JmixMultiSelectComboBoxPicker.class);
                    picker.setItems(context.getItem().getOrderItems());

                    PickerAction lookup = actions.create(EntityLookupAction.ID);
                    lookup.setTarget(picker);
                    PickerAction clear = actions.create(MultiEntityClearAction.ID);
                    clear.setTarget(picker);

                    picker.addAction(lookup);
                    picker.addAction(clear);

                    picker.setValueSource(context.getValueSourceProvider().getValueSource("orderItems"));

                    return picker;
                });
    }

Как вы можете заметить, вместо обычного EntityClearAction я использовал MultiEntityClearAction. Это связано с тем, что при попытке удаления значения в компоненте с использованием EntityClearAction происходит ошибка. Это тоже баг, на исправления которого я завёл задачу: EntityClearAction doesn't support MultiValueComboBoxPicker component · Issue #3709 · jmix-framework/jmix · GitHub

Баг связан с тем, что EntityClearAction не поддерживает удаление коллекции. В логике действия вызывается dataContext.remove(picker.getValue()), где в обычном случае getValue() возвращает коллекцию сущности. А в Вашем случае picker.getValue() вернёт коллекцию композитных сущностей.

Чтобы это решить, пришлось переписать функционал стандартного действия. Я расширил EntityClearAction и создал в проекте свой класс MultiEntityClearAction:

Посмотреть код
@ActionType(MultiEntityClearAction.ID)
public class MultiEntityClearAction<E> extends EntityClearAction<E> {

    public static final String ID = "multi_entity_clear";

    @Override
    public void execute() {
        ValueSource<E> valueSource = target.getValueSource();
        HasValue<?, E> hasValue = (HasValue<?, E>) target;

        if (!hasValue.isEmpty()
                && valueSource instanceof EntityValueSource) {
            EntityValueSource<?, ?> entityValueSource = (EntityValueSource<?, ?>) valueSource;
            entityValueSource.getMetaPropertyPath();
            if (entityValueSource.getMetaPropertyPath().getMetaProperty().getType() == MetaProperty.Type.COMPOSITION) {
                View view = UiComponentUtils.findView(((Component) target));
                if (view != null) {
                    DataContext dataContext = ViewControllerUtils.getViewData(view).getDataContext();

                    if (hasValue.getValue() instanceof Collection<?> collectionValue) {
                        collectionValue.forEach(dataContext::remove);
                    } else {
                        dataContext.remove(hasValue.getValue());
                    }
                }
            }
        }

        target.setValueFromClient(getEmptyValue((Component) target));
    }
}

Чтобы зарегистрировать MultiValueClearAction Вам потребуется указать пакет для сканирования кастомных Actions в классе конфигурации приложения (главном классе SpringBoot приложения):

    @Bean("my_UiActions")
    public ActionsConfiguration actions(ApplicationContext applicationContext,
                                        AnnotationScanMetadataReaderFactory metadataReaderFactory) {
        ActionsConfiguration actionsConfiguration = new ActionsConfiguration(applicationContext, metadataReaderFactory);
        actionsConfiguration.setBasePackages(Collections.singletonList("com.company.project.actions"));
        return actionsConfiguration;
    }

Надеюсь этот обходной путь Вам поможет, пока мы решаем задачи на исправления этих багов.

С уважением,
Дмитрий

1 симпатия

Большое спасибо за обратную связь. Буду пробовать.