4.5.3.3. Слушатели источников данных

Слушатели источников данных (datasource listeners) позволяют получать оповещения об изменении состояния источников данных и экземпляров сущностей, в них находящихся.

Для регистрации слушателей используются методы Datasource.addListener(), Datasource.removeListener(). Пример регистрации слушателя в контроллере экрана:

@Inject
private Datasource<Customer> customerDs;
...
public void init(Map<String, Object> params) {
  ...
  customerDs.addListener(new DatasourceListener<Customer>() {
      // listener methods implementation
  });
}

Существует два интерфейса слушателей источников данных: DatasourceListener и CollectionDatasourceListener. Первый можно использовать для регистрации в любых источниках данных, второй - только в реализующих CollectionDatasource. Как правило, на практике требуется получать не все оповещения от слушателя, а только некоторые. Поэтому удобно вместо реализации самих интерфейсов слушателей использовать классы-адаптеры DsListenerAdapter и CollectionDsListenerAdapter, содержащие пустые реализации всех методов соответствующих интерфейсов.

Рассмотрим методы DatasourceListener:

  • valueChanged() - объявление этого метода наследуется от базового интерфейса ValueListener. Данный метод слушателя вызывается, если изменилось значение какого-либо атрибута сущности, находящейся в данный момент в источнике. В метод передается сам измененный экземпляр, имя измененного атрибута, старое и новое значение.

    Оповещение valueChanged() можно использовать для действий в ответ на изменение пользователем сущности из UI, то есть редактирования полей ввода. В следующем примере гипотетический метод updateSettings() будет вызван при изменении значения атрибута active, и в него будет передано новое значение этого атрибута:

    @Inject
    private Datasource<Customer> customerDs;
    
    public void init(Map<String, Object> params) {
      ...
      customerDs.addListener(new DsListenerAdapter<Customer>() {
          @Override
          public void valueChanged(Customer source, String property, Object prevValue, Object value) {
              if ("active".equals(property)) {
                  boolean active = BooleanUtils.isTrue((Boolean) value); // converting null to false
                  updateSettings(active);
              }
          }
      });
    }
  • itemChanged() - вызывается при смене выбранного экземпляра, возвращаемого методом getItem().

    Для Datasource это происходит при установке другого экземпляра (или null) методом setItem().

    Для CollectionDatasource данное оповещение вызывается, когда в связанном визуальном компоненте меняется выделенный элемент. Например, это может быть выделенная строка таблицы, элемент дерева, или выделенный элемент выпадающего списка.

    Пример использования оповещения itemChanged()для управления состоянием действия таблицы:
    @Inject
    protected CollectionDatasource<Customer, UUID> customersDs;
    
    @Named("customersTable.remove")
    protected RemoveAction removeAction;
    
    public void init(Map<String, Object> params) {
      ...
      customersDs.addListener(new DsListenerAdapter<Customer>() {
          @Override
          public void itemChanged(Datasource<Customer> ds, Customer prevItem, Customer item) {
              removeAction.setEnabled(canCustomerBeDeleted(item));
          }
      });
    }
  • stateChanged() - вызывается при изменении состояния источника данных. Источник данных может находиться в одном из трех состояний, соответствующих перечислению Datasource.State:

    • NOT_INITIALIZED - источник только что создан.

    • INVALID - создан весь DsContext , к которому относится данный источник.

    • VALID - источник данных в рабочем состоянии: Datasource содержит экземпляр сущности или null, CollectionDatasource - коллекцию экземпляров или пустую коллекцию.

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

    @Inject
    protected CollectionPropertyDatasourceImpl<CategoryAttribute, UUID> categoryAttrsDs;
    
    categoryAttrsDs.addListener(new DsListenerAdapter<CategoryAttribute>() {
      @Override
      public void stateChanged(Datasource ds, Datasource.State prevState, Datasource.State state) {
          if (state != Datasource.State.VALID) return;
          initDataTypeColumn();
          initDefaultValueColumn();
      }
    });

Интерфейс CollectionDatasourceListener добавляет еще один метод:

  • collectionChanged() - вызывается при изменении коллекции сущностей, хранящейся в источнике данных. В метод передается тип изменения: REFRESH, CLEAR, ADD, REMOVE, UPDATE.

    Пример слушателя, вызывающего пересчет стоимости поездки при изменении адреса остановки (сущность Stop) или количества остановок:

    protected class StopDsListener extends CollectionDsListenerAdapter<Stop> {
      @Override
      public void valueChanged(Stop source, String property, Object prevValue, Object value) {
          // existing stop address changed
          if ("address".equals(property)) {
              fireRouteChanged();
          }
      }
    
      @Override
      public void collectionChanged(CollectionDatasource ds, Operation operation) {
          // stop was added or removed
          fireRouteChanged();
      }
    
      private void fireRouteChanged() {
          // journey route has changed, need to recalculate price, journey time, pickup time delay etc.
      }
    }