Все статьи

Выпущена версия Jmix 2.1

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

Полная информация об изменениях доступна на странице документации Что нового.

Новые дополнения

Мы реализовали ряд дополнений, которые ранее были доступны в Jmix v.1 с Classic UI. Теперь вы можете легко включить ту же готовую к использованию функциональность в ваш проект с современным Flow UI на основе Vaadin 24.

Maps

Дополнение Maps было переписано с нуля. Оно предлагает новый API и имеет отличную поддержку в Studio.

Вот пример, демонстрирующий отображение OpenStreetMap с маркером в определенной точке:

<maps:geoMap id="map" width="100%" height="100%">  
    <maps:layers>  
        <maps:tile>  
            <maps:osmSource/>  
        </maps:tile>  
        <maps:vector id="pointsLayer">  
            <maps:vectorSource/>  
        </maps:vector>  
    </maps:layers>  
</maps:geoMap>
@ViewComponent  
private GeoMap map;

@Subscribe  
public void onInit(InitEvent event) {  
    VectorLayer vectorLayer = map.getLayer("pointsLayer");  
    VectorSource vectorSource = vectorLayer.getSource();  
    vectorSource.addFeature(
		new MarkerFeature(GeometryUtils.createPoint(12.496176, 41.902695)));  
}

maps-1.png

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

Dynamic Attributes

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

Например, сущность Book может быть классифицирована по двум категориям: Electronic и Paper. У электронных книг есть атрибуты Available Formats и File Size, в то время как у бумажных книг атрибуты Cover Type, Weight и Dimensions.

Динамические атрибуты можно определять в пользовательском интерфейсе приложения:

dyn-attr-1.png

После настройки атрибутов пользователи могут просматривать и вводить их в существующих экранах:

dyn-attr-2.png

Динамические атрибуты автоматически отображаются либо в специальном компоненте dynamicAttributesPanel (как показано выше), либо в любом существующем formLayout и dataGrid.

Notifications

Дополнение Notifications предназначено для отправки пользователям уведомлений в приложении или по электронной почте. Уведомление можно отправить через API или с помощью предоставленного дополнением экрана:

notifications-2.png

UI приложения должен включать компонент notificationsIndicator, например в экране main-view.xml.

<appLayout>  
    <navigationBar>  
        <header ...>
	        ...
            <ntf:notificationsIndicator classNames="ms-auto me-m"/>  
        </header>  
    </navigationBar>

Получатели смогут увидеть количество непрочитанных уведомлений в индикаторе и открыть сообщения в удобном UI:

notifications-1.png

Search

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

Задайте подключение к Elasticsearch:

jmix.search.elasticsearch.url = http://localhost:9200

Создайте аннотированный Java интерфейс, описывающий, что должно быть проиндексировано:

@JmixEntitySearchIndex(entity = Book.class)  
public interface BookIndexDef {  
  
    @AutoMappedField(includeProperties =  
            {"title", "author", "publisher", "genre.name"})  
    void bookMapping();  
}

Добавьте компонент searchField в UI приложения:

<drawerLayout>  
    <section ...>
		<search:searchField/>

На этом всё. Данные сущности Book будут автоматически индексироваться (и переиндексироваться при каждом изменении), и пользователи смогут выполнять полнотекстовые запросы по созданному индексу:

search-1.png

Результаты поиска автоматически фильтруются в соответствии с разрешениями на доступ к данным текущего пользователя, исключая возможность утечки информации.

WebDAV

Дополнение WebDAV обеспечивает доступ к файлам, расположенным в файловом хранилище, по протоколу WebDAV. Это позволяет пользователям без проблем открывать и редактировать файлы с помощью их десктопных приложений (Word, Excel, LibreOffice и т. д.) без необходимости скачивать и снова загружать файлы в веб-приложение. На уровне пользовательского интерфейса дополнение предоставляет специальный компонент для загрузки и административный экран для управления файлами и их версиями:

webdav-1.png

Bulk Editor

Дополнение Bulk Editor позволяет пользователям изменять значения атрибутов сразу для нескольких экземпляров сущностей. Оно предоставляет действие, которое можно добавить в любой dataGrid:

<dataGrid id="booksDataGrid" ...>  
    <actions>
	    ...
        <action id="bulkEdit" type="bulked_edit"/>  
    </actions>

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

bulk-edit.png

JMX Console

Дополнение JMX Console предоставляет веб-интерфейс к API Java Management Extensions (JMX). Оно позволяет системным администраторам просматривать JMX-бины, редактировать атрибуты и вызывать операции прямо в пользовательском интерфейсе приложения:

jmx-console-1.png
jmx-console-2.png

Улучшения BPM

В пользовательском интерфейсе приложения теперь доступен DMN table modeler:

dmn-modeler.png

Мастер создания процессных форм в Studio теперь может генерировать полностью работоспособные экраны для редактирования переменных процесса и выбора выходов.

Мастер показывает переменные, определенные в процессе:

bpm-form-wizard-1.png

и позволяет задать выходы:

bpm-form-wizard-2.png

На основе вашего выбора, мастер генерирует код экрана для инжектирования переменных процесса в UI-компоненты и завершения задачи с выбранным выходом:

@ProcessForm(outcomes = {  
        @Outcome(id = "submit"),  
        @Outcome(id = "reject")  
})
// ...
public class BpmProcessForm extends StandardView {  
  
    @Autowired  
    private ProcessFormContext processFormContext;  
  
    @ProcessVariable  
    @ViewComponent    
    private EntityPicker<Book> book;  

	// ...	
    @Subscribe("submitBtn")  
    protected void onSubmitBtnClick(ClickEvent<JmixButton> event) {  
        processFormContext.taskCompletion()  
                .withOutcome("submit")  
                .saveInjectedProcessVariables()  
                .complete();  
        closeWithDefaultAction();  
    }  

Улучшения Data Grid

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

Теперь пользователи могут сортировать dataGrid по нескольким столбцам. Порядок сортировки обозначается числами, отображаемыми рядом со стрелками сортировки:

data-grid-sort-1.png

Сортировка определяется атрибутами multiSort, multiSortOnShiftClickOnly и multiSortPriority компонента dataGrid.

Еще одна новая функция - агрегация значений в строках. Для настройки агрегации для столбца установите атрибут aggregatable компонента dataGrid в true, добавьте элемент aggregation к столбцу и выберите тип агрегации. Агрегированные значения будут отображаться в отдельной строке:

data-grid-aggregation-1.png

Следующее улучшение - способность декларативно присваивать рендереры колонкам dataGrid. Те, кто знаком с Jmix Classic UI, обнаружат, что колонки с декларативными рендерерами очень похожи на "генерируемые колонки" в Classic UI. Вы определяете колонку в XML и создаете метод-обработчик, который возвращает рендерер:

@Supply(to = "stepsDataGrid.completed", subject = "renderer")  
private Renderer<UserStep> stepsDataGridCompletedRenderer() {  
    return new ComponentRenderer<>(userStep -> {
	    // ...
        return checkbox;  
    });  
}    

Есть несколько предустановленных рендереров, которые могут быть присвоены колонке в XML для форматирования дат и чисел. Кроме того, теперь колонка может быть определена в XML без привязки к атрибуту сущности, только для того, чтобы объявить для нее рендерер.

И, возможно, самой интересной новой функцией dataGrid является фильтр в заголовках колонок. Вы можете определить, какие колонки должны иметь фильтр, используя атрибут filterable элемента XML column. Колонки со включенной возможностью фильтрации имеют значок "воронки" в своих заголовках. Если пользователь нажимает на этот значок, появляется диалоговое окно с условием фильтрации:

data-grid-filter-1.png

Если условие установлено, значок в этой колонке выделяется:

data-grid-filter-2.png

Концепция фильтра в заголовках колонок знакома пользователям по многим популярным продуктам, включая Excel, поэтому эта функция легко обнаруживается и используется. Мы считаем, что такой подход к фильтрации данных является отличным дополнением к двум другим компонентам, предназначенным для выполнения той же задачи: genericFilter и propertyFilter. Компонент genericFilter настраивается полностью во время выполнения и позволяет создавать сложные условия, но далеко не самый простой в использовании. С другой стороны, propertyFilter прост и понятен для пользователей, но требует настройки разработчиком заранее. Фильтр в заголовках колонок таблицы похож на propertyFilter по возможностям, но не занимает пространства на экране и поэтому может быть сконфигурирован по умолчанию для большинства экранов, отображающих таблицы данных.

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

Новые UI-компоненты и фасеты

VirtualList

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

virtualList может использоваться в экранах вместо dataGrid. Для этого определите компонент в XML и соедините его с контейнером коллекции данных:

<virtualList id="stepsVirtualList" itemsContainer="stepsDc"/>

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

Затем вам нужно создать метод-обработчик, который отображает содержимое элемента списка:

@Supply(to = "stepsVirtualList", subject = "renderer")  
private Renderer<Step> stepsVirtualListRenderer() {  
    return new ComponentRenderer<>(step -> {
	    // ...
        return hbox;  
    });  
}

Затратив некоторые усилия на вёрстку элементов virtualList, можно получить примерно такой экран:

virtual-list-1.png

Html

Компонент html позволяет вставлять в экраны произвольное HTML-содержимое. Содержимое может быть определено вложенным элементом content, в файле, расположенном в ресурсах проекта, или в пакете сообщений для легкой интернационализации.

Settings

Фасет settings сохраняет и восстанавливает настройки визуальных компонентов для текущего пользователя. Автоматически сохраняются параметры колонок dataGrid, состояние opened компонентов details и genericFilter, а также выбранный размер страницы для simplePagination. Просто добавьте фасет в экран и установите его атрибут auto="true", и фасет будет управлять настройками всех поддерживаемых компонентов экрана, у которых заданы идентификаторы.

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

Timer

Фасет timer предназначен для выполнения кода экрана через определенные временные интервалы. Он работает в потоке, обрабатывающем события пользовательского интерфейса и может обновлять компоненты представления. Чтобы использовать фасет, определите его параметры в XML и создайте обработчик для TimerActionEvent:

<timer id="timer" delay="2000" repeating="true" autostart="true"/>
@Subscribe("timer")  
public void onTimerTimerAction(final Timer.TimerActionEvent event) {
	// ...
}

Загрузка элементов выпадающих списков

Этот релиз предоставляет новый эффективный способ загрузки данных в компоненты с выпадающими списками, такие как comboBox, entityComboBox и multiSelectComboBox. Теперь вам не нужно определять контейнер коллекции элементов списка и загружать его заранее полным списком вариантов. Вместо этого вы можете определить вложенный элемент itemsQuery в самом компоненте и написать в нем запрос следующим образом:

<entityComboBox id="departmentField" property="department" pageSize="30">
    <itemsQuery class="com.company.onboarding.entity.Department"
                fetchPlan="_instance_name"
                searchStringFormat="(?i)%${inputString}%">
        <query>
            <![CDATA[select e from Department e 
            where e.name like :searchString order by e.name]]>
        </query>
    </itemsQuery>
</entityComboBox>

Запрос будет выполнен только когда пользователь откроет выпадающий список, и результаты будут ограничены pageSize строками (по умолчанию 50). Когда пользователь прокручивает список, данные загружаются страницами. Если пользователь вводит текст в поле, он используется для фильтрации результатов.

Вместо написания запроса JPQL в XML, вы можете определить обработчик itemsFetchCallback и программно загрузить данные из любого источника.

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

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

Улучшения дизайнера экранов

Теперь давайте обсудим новые функции и улучшения в Студии.

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

Вы сможете видеть дерево компонентов, изменять свойства компонентов или даже добавлять новые компоненты во время работы с Java-кодом в контроллере.

view-designer-1.png

Еще одно улучшение связано с панелью Preview.

Отображение предварительного просмотра требует сборки фронтенда и запуска Vaadin Development Mode Server, что может занимать много времени. Чтобы сэкономить время при открытии проекта, панель Preview теперь открывается только после нажатия кнопки Start Preview в верхней панели редактора XML. После активации, предварительный просмотр будет оставаться активным для всех последующих открытых экранов проекта. Чтобы отключить предварительный просмотр, просто нажмите кнопку Stop Preview.

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

Помощь в написании кода

В этом релизе мы представляем совершенно новый подход к инжекции зависимостей и UI-компонентов в классы экранов и бины Spring.

Как только вы начнёте вводить символы внутри тела метода, появится выпадающий список автодополнения, показывающий локальные переменные, методы и поля класса, а кроме того - доступные бины и UI-компоненты. Бины и UI-компоненты, которые ещё не были инжектированы в класс, будут отображаться курсивом. Если вы выберете один из этих элементов, он будет автоматически инжектирован в конструктор или поле с соответствующей аннотацией (@Autowired или @ViewComponent), и станет сразу доступным для использования в текущей позиции курсора.

inject-by-code-completion-1.gif

С помощью этой функции найти и инжектировать доступные бины и UI-компоненты очень просто и быстро. Мы надеемся, что вам это понравится!

Поддержка репозиториев данных

Студия теперь полностью поддерживает создание и управление репозиториями Spring Data.

Вы можете создать репозиторий, нажав NewData Repository в окне инструментов Jmix. Студия создаст интерфейс репозитория и отобразит его внутри узла сущности.

Кнопки Add Derived Method и Add Query Method на панели действий редактора кода репозитория вызывают редактор методов репозитория с производными или явными запросами:

data-repositories-1.png

Для существующих методов репозитория Studio отображает слева иконку, нажатие на которую позволяет настроить параметры метода, например, добавить сортировку или фетч-план:

data-repositories-2.png

Добавление комментариев к модели данных

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

Дизайнер сущностей теперь содержит поля Comment как для самой сущности, так и для каждого атрибута. Вы можете добавлять комментарии, и они будут отображаться в этих полях:

data-model-comment-1.png

Текст, введенный в дизайнере, сохраняется в аннотациях @Comment класса сущности и его полей:

@Comment("""
        Stores information about books.
        Has reference to Genre.""")
@JmixEntity
@Table(name = "BOOK")
@Entity
public class Book {
	// ...
	
	@Comment("Book title")
	@Column(name = "TITLE", nullable = false)  
	private String title;

Студия генерирует операции setTableRemarks и setColumnRemarks в файлах Liquibase changelog для сохранения комментариев в схеме базы данных (для всех баз данных, кроме HSQL). Это позволяет получать доступ к комментариям через любые инструменты работы с базой данных. Вы также можете извлекать комментарии из метаданных или непосредственно из аннотаций класса, чтобы отображать их в пользовательском интерфейсе приложения или генерировать документацию.

Что дальше?

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

Со стороны Studio мы предоставим hot deploy BPMN-определений бизнес-процессов, генерацию пользовательских REST-контроллеров и интеграционных тестов для ваших сущностей и экранов.

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

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

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

Jmix - это open-source платфора быстрой разработки бизнес-приложений на Java