Неявные изменения редактируемой сущности

Добрый день!
На редакторе некоторой сущности на кнопке закрыть висит следующий код:

  public void onWindowCloseBtnClick() {
        if (super.isModifiedAfterOpen())
            dialogs.createOptionDialog()
                    .withCaption("Закрытие редактирования точки поставки")
                    .withMessage("Сохранить данные?")
                    .withActions(new DialogAction(DialogAction.Type.YES, Action.Status.PRIMARY)
                                    .withIcon("YES")
                                    .withHandler(e -> onWindowCommitAndCloseBtnClick()),
                            new DialogAction(DialogAction.Type.NO)
                                    .withIcon("CLOSE")
                                    .withHandler(e -> this.closeWithDiscard())
                    ).show();
        else
            this.closeWithDiscard();
    }

Работает штатно, до тех пор пока я не открываю вкладку, где находится фрагмент с таблицей сущностей EntityLog. После открытия этой вкладки, при нажатии на кнопку он видит какие то изменения и выводит диалоговое окно, хотя никаких изменений внесено не было. Причем даже если нажать ДА на диалоговом окне и сохранить эти фантомные изменения, по факту ничего изменено не будет. Может есть какие то мысли откуда берутся эти изменения?
Контроллер фрагмента из вкладки:

public class EntityLogFragment extends ScreenFragment {
    @Inject
    private CollectionLoader<EntityLogItem> entityLogItemsDl;
    @Inject
    private Table<EntityLogItem> logsTable;
    private UUID entityId;
    @Inject
    private Fragments fragments;
    @Inject
    private CollectionContainer<EntityLogItem> entitylogsDc;

    @Subscribe
    public void onInit(InitEvent event) {
        entityLogItemsDl.setParameter("entity", entityId);
        entityLogItemsDl.load();
    }


    public void setEntityId(UUID entityId) {
        this.entityId = entityId;
    }

    public Component generateAttrTableCell(EntityLogItem entity) {
        EntityLogAttributesTable attributesTable = fragments.create(this, EntityLogAttributesTable.class);
        attributesTable.getFragment().setWidthFull();
        attributesTable.getFragment().setHeightFull();
        attributesTable.setAttributes(entity.getAttributes());
        String title = entity.getEventTs().toLocaleString() + ", Пользователь: " + entity.getUser().getName()+ " (" + entity.getUser().getLogin()+")";
        attributesTable.setTitle(title);
        return attributesTable.getFragment();
    }
}

Контроллер вложенного фрагмента

public class EntityLogAttributesTable extends ScreenFragment {

    @Inject
    private CollectionLoader<EntityLogAttr> entityLogItemsDl;
    @Inject
    private CollectionContainer<EntityLogAttr> entitylogsDc;
    @Inject
    private GroupBoxLayout logBox;


    public void setAttributes(Set<EntityLogAttr> attributes){
        List<EntityLogAttr> attrToShow = new ArrayList<>();
        for (EntityLogAttr attr : attributes){
            if (attr.getOldValue() != null && attr.getValue() != null)
                attrToShow.add(attr);
        }
        entitylogsDc.setItems(attrToShow);
    }
    public void setTitle(String s){
        logBox.setCaption(s);
    }
}

На другом экране все работает корректно, никакие изменения не появляются. Однако этот экран является AbstractEditor и кнопка закрыть вызывает действие

super.close(CLOSE_ACTION_ID);

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

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

Попробуйте поставить точки остановы в разных местах фрагментов и экрана и посмотреть что меняет контекст данных. Если столкнётесь с затруднениями, то, пожалуйста, пришлите небольшой демо проект в котором данная проблема воспроизводится (по коду очень сложно определить что не так).

Отслеживать изменения переменной dataContext экрана?

Для начала можно попробовать поставить останову в StandardEditor#onChangeEvent() именно там происходит простановка setModifiedAfterOpen(true).
Еще можно посмотреть DataContextImpl#fireChangeListener() там публикуются события изменения контекста.

1 симпатия

Из документации:
"Измененные значения атрибутов хранятся в этой же таблице в колонке CHANGES , а при чтении на Middleware преобразуются в экземпляры сущности EntityLogAttr"
Судя по всему при этом преобразовании (т.е. создании сущностей EntityLogAttr по данным колонки changes) и происходит изменение контекста, так как по факту создаются новые сущности и добавляются в таблицу. В режиме дебага я и видел эти новые сущности атрибутов, которые вызывали публикацию ивента onChange.
Можно ли как то обойти это или сделать, чтобы конкретно здесь не было зарегестрировано изменения контекста?

Как вариант, в элементе <data> фрагмента можно добавить readOnly=true атрибут. Почитать об этом можно в документации.

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

 super.close(CLOSE_ACTION_ID);

из AbstractEditor? Это действие работает в точности как нужно. Отслеживает изменения контекста и если они есть выводит диалоговое сообщение, причем на эти создания entityLogAttr как будто не обращает внимания.

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

testEntityLog.rar (303.1 КБ)
Здравствуйте! Прикрепляю тестовый проект с воспроизведенной проблемой.

1 симпатия

Здравствуйте!
Спасибо за демо проект, проблему воспроизвёл. Попробуйте перенести код добавления фрагмента из onAfterShow() в слушатель события onBeforeShow(). Дело в том, что во время события BeforeShowEvent загрузка происходит до добавления слушателя изменения данных, таким образом она поволяет отслеживать реальные изменения сущности. На момент AfterShowEvent прослушиваются все изменения контекста.
При добавлении фрагмента не обязательно вручную вызывать onInit() метод. Он должен вызываться автоматически при добавлении фрагмента к экрану.

Большое спасибо, вроде работает!
Не подскажите еще, можно ли как то не выводить атрибуты, у которых пустые старые и новые значения?
Вроде я пытался реализовать некий фильтр (в тестовом проекте он тоже присутствует), однако похоже он не работает. Скорее всего это именно из за процедурной генерации самих сущностей атрибутов.
image

Если считать что пустая строка в атрибуте сущности равна null, то можно добавить еще проверку на пустую строку.

if (!isNullOrEmpty(attr.getOldValue()) || !isNullOrEmpty(attr.getValue())) {
    attrToShow.add(attr);
}

Спасибо за ответы!