Эта статья рассказывает о ключевых обновлениях, которые были включены в недавно опубликованный релиз Jmix 2.3. Полные сведения об изменениях и советы по обновлению проектов можно найти в разделе документации Что нового.

Дополнение Superset

Apache Superset - это ведущий открытый программный продукт для исследования и визуализации данных. Он позволяет создавать легко настраиваемые дэшборды, содержащие различные графики. Графики заполняются из датасетов, которые получают данные из вашей БД с помощью SQL-запросов.

Дополнение Jmix Superset позволяет легко подключить ваше приложение к серверу Apache Superset и встроить дэшборды в экраны Jmix. Например, основной экран приложения Bookstore может содержать дэшборд с агрегированной информацией о заказах клиентов:

Достаточно указать URL-адрес и учетные данные пользователя для подключения к серверу Superset, и дополнение позаботится о запросе, обновлении и использовании токенов безопасности при отображении встроенных дэшбордов.

После создания дэшборда в Superset, его нужно подготовить к встраиванию. Superset генерирует идентификатор для ссылки на этот дэшборд извне.

UI-компонент dashboard, предоставляемый дополнением, позволяет включить дэшборд в экран приложения. Вам нужно только указать идентификатор дэшборда в атрибуте embeddedId:

<superset:dashboard id="ordersDashboard" 
		embeddedId="4bc14bf5-a3ec-4151-979e-a920420e1f66"  
        height="100%" width="100%" maxWidth="50em"/>

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

@Install(to = "ordersDashboard", subject = "datasetConstraintsProvider")  
private List<DatasetConstraint> ordersDashboardDatasetConstraintsProvider() {  
    DatasetConstraint constraint = new DatasetConstraint(25,  
            "tenant = '" + tenantProvider.getCurrentUserTenantId() + "'");  
    return List.of(constraint);  
}

Для получения подробной информации об использовании дополнения Superset с вашим Jmix-приложением см. документацию по дополнению Superset.

Поддержка OpenSearch

Теперь вы можете использовать дополнение Jmix Search с сервисом OpenSearch. Все функции дополнения (декларативные определения индексов, очередь индексации, поле поиска в UI и т.д.) работают одинаково как с движком OpenSearch, так и с Elasticsearch. Выбор между OpenSearch и Elasticsearch сводится к указанию соответствующего стартера в зависимостях build.gradle. Когда дополнение устанавливается из Marketplace, по умолчанию выбирается OpenSearch.

Фрагменты

Фрагмент - это новый строительный блок пользовательского интерфейса, предназначенный для повторного использования кода и декомпозиции сложных структур UI. С точки зрения функциональности, фрагменты находятся между экранами и составными компонентами.

Так же, как и экран, фрагмент может иметь XML-дескриптор с компонентами данных и действиями. Он поддерживает внедрение компонентов и обработчики событий в классе фрагмента. Студия предоставляет шаблон для создания пустого фрагмента и тот же UI-дизайнер, что и для экранов.

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

Цель фрагментов - предоставить больше гибкости в структурировании и повторном использовании кода UI, связанного с моделью данных.

Приведенный ниже пример дает представление о том, как создавать и использовать фрагменты. Этот простой фрагмент представляет данные встраиваемой сущности Money. UI-дизайнер с XML-кодом фрагмента и предварительным просмотром выглядит следующим образом:

Класс фрагмента (также известный как контроллер) может определять сеттеры для приема параметров из хост-экрана (или хост-фрагмента). В нашем случае он получает экземпляр Money и устанавливает его в свой собственный контейнер данных:

@FragmentDescriptor("money-fragment.xml")  
public class MoneyFragment extends Fragment<JmixFormLayout> {  
  
    @ViewComponent  
    private InstanceContainer<Money> moneyDc;  
  
    public void setMoney(Money money) {  
        moneyDc.setItem(money);  
    }  
}

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

<vbox>  
    <h4 text="msg://price"/>  
    <fragment id="unitPriceFragment" 
		    class="io.jmix.bookstore.view.moneyfragment.MoneyFragment"/>  
</vbox>  
<vbox>  
    <h4 text="msg://discount"/>  
    <fragment id="discountFragment" 
		    class="io.jmix.bookstore.view.moneyfragment.MoneyFragment"/>  
</vbox>

Контроллер хост-экрана вызывает методы API фрагмента, чтобы передать экземпляры Money:

@ViewComponent  
private MoneyFragment unitPriceFragment;  
@ViewComponent  
private MoneyFragment discountFragment;

@Subscribe  
public void onReady(final ReadyEvent event) {  
    unitPriceFragment.setMoney(orderLineDc.getItem().getUnitPrice());  
    discountFragment.setMoney(orderLineDc.getItem().getDiscount());  
}

Репозитории данных

Этот релиз завершает реализацию поддержки репозиториев Spring Data в качестве "граждан первого сорта" в экосистеме Jmix. Теперь вы можете легко использовать их в экранах для загрузки и сохранения данных, сохраняя совместимость со всеми стандартными функциями пользовательского интерфейса Jmix, такими как фильтрация, разбивка на страницы и сортировка.

При создании экрана с помощью мастера, обратите внимание на флажок Use Data Repository в разделе Advanced. Если вы включите его, вы сможете выбрать существующий репозиторий данных. Мастер сгенерирует экран с делегатами загрузки и сохранения, вызывающими методы вашего репозитория.

Делегат загрузки списка извлечет Spring Data PageRequest из объекта Jmix LoadContext и предоставит условия фильтрации и другие параметры репозиторию данных в объекте JmixDataRepositoryContext. Вы можете изменить логику делегата, например, чтобы задать начальную сортировку:

@Install(to = "customersDl", target = Target.DATA_LOADER)  
private List<Customer> loadDelegate(LoadContext<Customer> context) {  
    // convert Jmix paging and sorting parameters to Spring Data PageRequest  
    PageRequest pageable = JmixDataRepositoryUtils.buildPageRequest(context);  
    if (pageable.getSort().isEmpty()) {  
        // set initial sorting
        pageable = pageable.withSort(Direction.ASC, "firstName", "lastName");  
    }  
    // provide Jmix conditions, fetch plan and hints
    JmixDataRepositoryContext jmixContext = JmixDataRepositoryUtils.buildRepositoryContext(context);  
    // invoke repository method and return the page content
    return repository.findAll(pageable, jmixContext).getContent();  

Приведенный выше код делает то же самое, что и следующий JPQL в загрузчике:

<loader id="customersDl" readOnly="true">  
    <query>  
        <![CDATA[select e from bookstore_Customer e  
        order by e.firstName asc, e.lastName asc]]>  
    </query>  
</loader>

Все методы репозиториев, которые расширяют JmixDataRepository, теперь поддерживают JmixDataRepositoryContext в качестве дополнительного параметра. Это делает репозитории данных совместимыми с компонентами genericFilter и propertyFilter и декларативными загрузчиками данных.

Ленивые вкладки TabSheet

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

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

Загрузка данных и другая инициализация для ленивых вкладок должна выполняться в слушателе SelectedChangeEvent компонента tabSheet, например:

@ViewComponent  
private CollectionLoader<Position> positionsDl;  
  
private boolean positionsInitialized;

@Subscribe("tabSheet")  
public void onTabSheetSelectedChange(final JmixTabSheet.SelectedChangeEvent event) {  
    if ("positionTab".equals(event.getSelectedTab().getId().orElse(null))  
            && !positionsInitialized) {  
        positionsDl.load();  
        positionsInitialized = true;  
    }  
}

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

Компонент TwinColumn

Новый компонент twinColumn является отличным дополнением к библиотеке UI-компонентов Jmix. Он предоставляет пользователям знакомый и удобный способ выбора элементов из списка:

Дополнение Authorization Server

Мы извлекли модуль Authorization Server в отдельное дополнение со своей документацией и процессом установки. Ранее он устанавливался вместе с дополнением Generic REST. Поэтому теперь, при установке Generic REST, вам необходимо решить, как вы будете защищать его эндпойнты: используя дополнения Authorization Server или OIDC, или другими средствами.

Новая функция дополнения Authorization Server - реализация гранта Resource Owner Password Credentials. Этот тип гранта не рекомендуется спецификацией OAuth, но мы получили много запросов от разработчиков приложений и решили реализовать его. Его можно использовать в доверенных, устаревших или строго контролируемых средах для простой аутентификации клиентов REST как зарегистрированных пользователей приложения Jmix.

Агрегация файлов Liquibase changelog

Самая заметная новая функция в Studio - это возможность агрегировать существующие журналы изменений Liquibase. Она позволяет разработчикам объединять несколько последних журналов изменений в один и избавляться от дублированных действий в наборах изменений.

Рассмотрим следующую ситуацию: в результате итеративной разработки модели данных у вас есть три журнала изменений. Первый добавляет столбец ALPHA, второй добавляет столбец BETA, а третий добавляет GAMMA и удаляет ALPHA. Таким образом, изменения, введенные в первом журнале, перекрываются третьим.

Действие Aggregate Liquibase Changelogs доступно в контекстном меню хранилища данных. Оно позволяет выбрать любое количество последних журналов изменений для агрегации. Давайте выберем три журнала изменений, описанных выше:

Результирующий журнал изменений будет включать только изменения, которые действительно приводят схему базы данных в соответствие с моделью данных: добавление столбцов BETA и GAMMA:

Studio удалит выбранные файлы журналов изменений и добавит новый агрегированный. Чтобы сохранить набор журналов изменений совместимым с базами данных, где старые журналы уже были выполнены, Studio может добавить предусловие к новым наборам изменений. Это предусловие инструктирует Liquibase не выполнять новые наборы изменений, если первый журнал изменений из замененного списка был выполнен:

<preConditions onFail="MARK_RAN">  
    <not>  
        <changeSetExecuted id="1" author="bookstore"  
			changeLogFile="io/jmix/bookstore/liquibase/changelog/2024/06/27-1-add-alpha.xml"/>  
    </not>  
</preConditions>

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

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

Другие улучшения в Studio

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

  • Окно инструментов Jmix предоставляет команду для генерации обработчиков исключений пользовательского интерфейса.

  • Команда Convert to в контекстном меню структуры Jmix UI позволяет преобразовать один компонент в другой одним щелчком. Команда Wrap into умеет оборачивать несколько выбранных компонентов во вкладку TabSheet.

  • Улучшена компоновка JPQL Designer.

  • Обновлена функция генерации Dockerfile.

См. список всех улучшений Studio в нашем багтрекере.

Заключение

В следующем функциональном релизе в октябре 2024 года мы планируем реализовать следующие новые возможности в Jmix UI:

  • Интегрировать несколько сторонних JavaScript-компонентов: Calendar, PivotTable, Kanban board.

  • Предоставить возможность использовать фрагменты для определения внутренней компоновки элементов VirtualList.

  • Реализовать дополнительный режим главного экрана, при котором несколько экранов можно открыть во вкладках внутри главного экрана, а не во вкладках браузера.

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

Еще одна запланированная функция, которая может помочь с миграцией с более старых версий платформы, - это декларативные разрешения на элементы UI. Эта функциональность присутствовала в CUBA Platform и позволяла разработчикам и администраторам приложений ограничивать доступ к произвольным частям пользовательского интерфейса (полям, кнопкам, действиям) без необходимости писать какой-либо код.

В Studio мы собираемся сделать использования инспектора Jmix UI более удобным и предоставить больше редакторов для свойств компонентов пользовательского интерфейса (например для formLayout.responsiveSteps). Кроме того, мы представим первые результаты нашей работы по интеграции с внешними источниками данных.

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

Наш подробный план для будущих версий доступен как проект GitHub. Патчи для текущей версии 2.3 будут выходить примерно раз в месяц.

Будем рады увидеть ваши отзывы на нашем форуме!

Спасибо всем, кто отправлял нам PR-ы, делился своими идеями, предложениями и сообщениями об ошибках!

Комментарии (0)