Шаблоны экранов времени выполнения
Шаблоны экранов времени выполнения позволяют генерировать стандартные экраны списка и деталей из шаблонов, объявленных в метаданных сущности, вместо того чтобы создавать их вручную на этапе разработки. Вы аннотируете класс сущности с помощью @ListViewTemplate и/или @DetailViewTemplate, и фреймворк генерирует соответствующие экраны, маршруты и пункты меню при запуске приложения.
Это удобно для быстрого отображения сущностей в UI – например, справочников или ссылочных данных – без написания классов экранов и XML-дескрипторов для каждой из них. Сгенерированные экраны регистрируются так же, как и обычные экраны: они участвуют в выводе экранов, могут быть открыты по идентификатору или маршруту и могут служить основными экранами списка и деталей для сущности.
| Шаблоны экранов времени выполнения в настоящее время находятся в экспериментальном состоянии. API и встроенные шаблоны могут существенно измениться в следующем релизе Jmix. |
Начало работы
Чтобы сгенерировать экран списка и деталей для сущности, аннотируйте её класс:
@ListViewTemplate(parentMenu = "application")
@DetailViewTemplate
@JmixEntity
@Entity
@Table(name = "PRODUCT")
public class Product {
С этими двумя аннотациями фреймворк генерирует при запуске:
-
экран списка с идентификатором
Product.list, отображающий dataGrid со свойствами сущности, genericFilter, постраничную навигацию и действия Create, Edit, Remove; -
экран деталей с идентификатором
Product.detail, отображающий formLayout с редактируемыми полями для свойств сущности; -
пункт меню в меню
application, открывающий экран списка.
Экраны используют встроенные шаблоны, которые решают, что отображать, на основе свойств сущности. В проекте не создаются никакие XML-дескрипторы или классы экранов.
@ListViewTemplate
@ListViewTemplate объявляет генерируемый экран списка для аннотированной сущности:
@ListViewTemplate(
path = "io/jmix/flowui/view/template/list-view.ftl",
viewId = "Supplier.list",
viewRoute = "suppliers",
viewTitle = "Suppliers",
parentMenu = "application",
templateParams = """
{
"includeProperties": ["createdBy", "createdDate"],
"excludeProperties": ["internalNote"]
}
"""
)
В дополнение к общим атрибутам он поддерживает:
-
lookupComponentId– идентификатор компонента, используемого в качестве компонента выбора. По умолчаниюdataGrid.
@DetailViewTemplate
@DetailViewTemplate объявляет генерируемый экран деталей для аннотированной сущности:
@DetailViewTemplate(
viewId = "Supplier.detail",
viewRoute = "suppliers",
viewTitle = "Supplier",
editedEntityContainerId = "entityDc"
)
В дополнение к общим атрибутам он поддерживает:
-
editedEntityContainerId– идентификатор контейнера данных, содержащего редактируемую сущность, см. @EditedEntityContainer. По умолчаниюentityDc.
Общие атрибуты
Обе аннотации используют следующие атрибуты:
| Атрибут | Описание |
|---|---|
|
Путь к ресурсу шаблона Freemarker XML-дескриптора. Если не указан, используется соответствующий встроенный шаблон. |
|
JSON-объект с дополнительными параметрами, передаваемыми в шаблон. См. встроенные шаблоны для параметров, поддерживаемых встроенными шаблонами. |
|
Идентификатор родительского пункта меню для генерируемого пункта меню. Если не указан, то пункт меню не создаётся. Если он указывает на отсутствующий пункт меню, автоматически создаётся новый корневой пункт меню. |
|
Идентификатор генерируемого экрана. |
|
Путь маршрута генерируемого экрана. |
|
Заголовок страницы генерируемого экрана. |
Значения по умолчанию
Когда необязательные атрибуты опущены, фреймворк выводит их из имени сущности <entityName>:
@ListViewTemplate |
@DetailViewTemplate |
|
|---|---|---|
|
|
|
|
|
|
Маршрутизация
Маршрут разрешается из viewRoute следующим образом:
-
Для экрана списка
viewRouteиспользуется как есть. -
Для экрана деталей
viewRouteтрактуется как базовый маршрут и всегда становится…/:id. -
Если
viewRouteпуст, он выводится изviewId.
В приведённом выше примере Supplier маршрут экрана списка – suppliers, а маршрут детального экрана – suppliers/:id.
Встроенные шаблоны
Когда path не указан, фреймворк использует встроенные шаблоны, которые генерируют содержимое экрана из свойств сущности:
-
шаблон экрана списка отображает
dataGridсо столбцом для каждого свойства,genericFilter, постраничную навигацию и действия списка; -
шаблон экрана деталей отображает
formLayoutс полем для каждого свойства.
Встроенные шаблоны расположены в следующих файлах ресурсов:
-
io/jmix/flowui/view/template/list-view.ftl -
io/jmix/flowui/view/template/detail-view.ftl
Фильтрация свойств
По умолчанию встроенные шаблоны включают непосредственные одиночные свойства и исключают:
-
системные свойства;
-
свойства, аннотированные как секретные;
-
свойства id, version и генерируемые свойства;
-
свойства аудита и мягкого удаления.
Встроенная фильтрация свойств поддерживает только непосредственные одиночные свойства типа данных, перечисления, ассоциации и композиции. Она не поддерживает коллекции элементов, встроенные свойства или составные пути свойств, такие как customer.name.
Вы можете скорректировать набор отображаемых свойств с помощью двух ключей в templateParams:
-
includeProperties– восстанавливает исключённые, но поддерживаемые непосредственные свойства; -
excludeProperties– удаляет свойства; применяется последним и имеет приоритет надincludeProperties.
В примере Supplier свойства аудита createdBy и createdDate восстановлены, а internalNote исключено:
@ListViewTemplate(
path = "io/jmix/flowui/view/template/list-view.ftl",
viewId = "Supplier.list",
viewRoute = "suppliers",
viewTitle = "Suppliers",
parentMenu = "application",
templateParams = """
{
"includeProperties": ["createdBy", "createdDate"],
"excludeProperties": ["internalNote"]
}
"""
)
Коллекции композиции
Если сущность, используемая в @DetailViewTemplate, имеет свойства композиции *-to-many, генерируемый экран деталей отображает tabSheet вместо одной формы:
-
первая вкладка General содержит форму с полями одиночных значений;
-
каждая коллекция композиции получает собственную вкладку с
dataGrid, предоставляющим действия Create, Edit и Remove.
Действия Create и Edit открывают собственный экран деталей сущности строки в диалоге, поэтому ожидается, что сущности строк также несут @DetailViewTemplate. Обратный атрибут коллекции композиции – ссылка дочерней сущности обратно на родительскую – по умолчанию исключается как из столбцов мастера, так и из формы деталей сущности строки. Вы можете восстановить его с помощью includeProperties.
В приведённом ниже примере определяются мастер-сущность с коллекцией композиции и соответствующая сущность строки:
@ListViewTemplate(parentMenu = "application")
@DetailViewTemplate
@JmixEntity
@Entity
@Table(name = "INVOICE")
public class Invoice {
@Composition
@OneToMany(mappedBy = "invoice")
private List<InvoiceItem> items;
@DetailViewTemplate
@JmixEntity
@Entity
@Table(name = "INVOICE_ITEM")
public class InvoiceItem {
Поддерживаются только коллекции композиции. Коллекции ассоциаций, коллекции элементов и встроенные атрибуты не отображаются.
Сущности без коллекций композиции отображают обычный formLayout.
Пользовательские шаблоны
Чтобы полностью контролировать содержимое генерируемого экрана, предоставьте собственный шаблон Freemarker в атрибуте path. Шаблон – это файл .ftl, расположенный в classpath, который создаёт стандартный XML-дескриптор экрана.
В модели шаблона доступны следующие переменные:
-
entityMetaClass–MetaClassаннотированной сущности; -
viewTitle– заголовок экрана; -
templateHelper– класс-помощник, который возвращает отфильтрованные свойства (getProperties) и свойства коллекций композиции (getCollectionProperties); -
componentXmlFactory– фабрика, которая создаёт XML-фрагмент для компонента редактирования свойства; -
все ключи из
templateParams, включаяincludePropertiesиexcludeProperties.
В качестве отправной точки вы можете использовать встроенные шаблоны.
Интеграция с меню
Если атрибут parentMenu аннотации @ListViewTemplate или @DetailViewTemplate задан, пункт меню, открывающий генерируемый экран, добавляется к указанному родительскому меню после загрузки стандартных XML-определений меню. Если parentMenu пуст, пункт меню не создаётся. Если он указывает на отсутствующий пункт меню, автоматически создаётся новый корневой пункт меню.