DataContext
Интерфейс DataContext позволяет отслеживать изменения в сущностях, загружаемых на уровень UI. Отслеживаемые сущности помечаются как "грязные" при любом изменении значений их атрибутов, и DataContext сохраняет грязные экземпляры при вызове его метода save().
Внутри DataContext сущность с некоторым идентификатором будет представлена единственным объектом, вне зависимости от того, где и сколько раз она использована в графах сущностей, находящихся в данном контексте.
Чтобы сущность отслеживалась, ее необходимо поместить в DataContext с помощью метода merge(). Если контекст не содержит экземпляра сущности с таким же идентификатором, то контекст создает новый экземпляр и копирует в него состояние переданного. Если контекст уже содержит экземпляр сущности с таким же идентификатором, он копирует в имеющийся экземпляр состояние переданного и возвращает имеющийся экземпляр. Данный механизм позволяет всегда иметь в контексте не более одного экземпляра сущности с конкретным идентификатором.
При помещении сущности в контекст методом merge() весь граф объектов с корнем в данной сущности также помещается в контекст. То есть все связанные сущности, включая коллекции, становятся отслеживаемыми.
| Главный принцип использования метода merge()заключается в том, чтобы продолжать работать с возвращенным из метода экземпляром, забывая про переданный. В большинстве случаев возвращенный экземпляр будет другим. Единственное исключение – если вmerge()передан экземпляр объекта, ранее возвращенный другим вызовомmerge()илиfind()этого же контекста. | 
Пример помещения сущности в DataContext:
@ViewComponent
private CollectionContainer<Department> departmentsDc;
@Autowired
private DataManager dataManager;
@ViewComponent
private DataContext dataContext;
private void loadDepartment(Id<Department> departmentId) {
    Department department = dataManager.load(departmentId).one();
    Department trackedDepartment = dataContext.merge(department);
    departmentsDc.getMutableItems().add(trackedDepartment);
}Для некоторго экрана существует только один экземпляр DataContext. Он создается автоматически, если в XML-дескрипторе экрана есть элемент <data>.
Загрузчики данных, определенные в XML, автоматически помещают загруженные сущности в DataContext, если у них нет атрибута readOnly="true". По умолчанию загрузчики данных в экранах списков сущностей, создаваемых Studio, включают этот атрибут. Поэтому, если вам нужно отслеживать изменения и сохранять измененные сущности в экранах списков, удалите атрибут readOnly="true" у соответствующих загрузчиков.
Получение DataContext
- 
DataContextэкрана можно получить в его контроллере используя инжектирование:@ViewComponent private DataContext dataContext;
- 
Если имеется ссылка на некоторый экран, то получить его DataContextможно с помощью классаViewControllerUtils:private void sampleMethod(View sampleView) { DataContext dataContext = ViewControllerUtils.getViewData(sampleView).getDataContext(); // ... }
Родительский DataContext
Сущности DataContext могут образовывать отношения предок-потомок. Если у экземпляра DataContext есть родительский контекст, он будет сохранять измененные сущности в своего предка вместо того, чтобы сразу отправлять их в хранилище данных. Эта особенность позволяет редактировать агрегаты, когда дочерние сущности должны сохраняться только вместе с родительской. Если атрибут сущности снабжен аннотацией @Composition, фреймворк автоматически установит родительский контекст для экрана деталей этого атрибута, чтобы измененная сущность атрибута сохранялась в контекст сущности-владельца.
Подобное поведение можно легко настроить вручную для любой сущности или экрана.
Если вы программно открываете экран деталей сущности, который должен сохранять изменения в data context текущего экрана, используйте метод withParentDataContext():
private void detailViewWithCurrentDataContextAsParent() {
    DialogWindow<DepartmentDetailView> dialogWindow = dialogWindows.detail(this, Department.class)
            .withViewClass(DepartmentDetailView.class)
            .withParentDataContext(dataContext)
            .build();
    dialogWindow.open();
}Дополнительные примеры явного использования DataContext приведены в руководстве Data Modeling: Composition.
События и обработчики
В этом разделе описываются события жизненного цикла DataContext, на которые можно подписаться в контроллерах экрана.
| Чтобы сгенерировать заглушку обработчика в Jmix Studio, выберите элемент данных в XML-коде дескриптора экрана или на панели структуры Jmix UI и используйте вкладку Handlers панели инспектора Jmix UI. В качестве альтернативы вы можете воспользоваться кнопкой Generate Handler на верхней панели контроллера экрана. | 
SaveDelegate
По умолчанию DataContext сохраняет измененные и удаленные объекты с помощью метода DataManager.save(SaveContext). Обработчик saveDelegate позволяет настраивать логику сохранения данных, что особенно полезно при работе с DTO сущностями. Например, вы можете сохранить измененные объекты с помощью специального сервиса:
@Autowired
private DepartmentService departmentService;
@Install(target = Target.DATA_CONTEXT)
private Set<Object> saveDelegate(final SaveContext saveContext) {
    return departmentService.saveEntities(
            saveContext.getEntitiesToSave(),
            saveContext.getEntitiesToRemove());
}Обработчик saveDelegate должен возвращать Set сохраненных экземпляров. Если это невозможно, верните исходные экземпляры из saveContext.getEntitiesToSave() или просто пустой Set. Не возвращайте удаленные экземпляры. Возвращенные экземпляры будут помещены обратно в DataContext, и экран продолжит работу с обновленным состоянием.
См. раздел Работа с сущностями в UI для дополнительной информации о сохранении сущностей с использованием кастомных сервисов.
ChangeEvent
Это событие отправляется, когда DataContext обнаруживает изменения в отслеживаемой сущности, в контекст помещается новый экземпляр, или при удалении сущности.
@Subscribe(target = Target.DATA_CONTEXT)
public void onChange(final DataContext.ChangeEvent event) {
    log.debug("Changed entity: " + event.getEntity());
}PreSaveEvent
Это событие отправляется перед сохранением изменений.
В слушателе этого события можно добавлять произвольные экземпляры сущностей в сохраняемые коллекции, возвращаемые методами getModifiedInstances() и getRemovedInstances(), например:
@Subscribe(target = Target.DATA_CONTEXT)
public void onPreSave(final DataContext.PreSaveEvent event) {
    event.getModifiedInstances().add(department);
}Вы также можете предотвратить сохранение, используя метод события preventCommit(), например:
@Subscribe(target = Target.DATA_CONTEXT)
public void onPreSave2(DataContext.PreSaveEvent event) {
    if (checkSomeCondition()) {
        event.preventSave();
    }
}PostSaveEvent
Это событие отправляется после сохранения изменений.
Из слушателя этого события можно получить коллекцию сохраненных сущностей, возвращенных из DataManager или собственного save delegate. Эти сущности уже помещены в DataContext. Например:
@Subscribe(target = Target.DATA_CONTEXT)
public void onPostSave(final DataContext.PostSaveEvent event) {
    log.debug("Saved: " + event.getSavedInstances());
}