Шаблоны экранов времени выполнения

Шаблоны экранов времени выполнения позволяют генерировать стандартные экраны списка и деталей из шаблонов, объявленных в метаданных сущности, вместо того чтобы создавать их вручную на этапе разработки. Вы аннотируете класс сущности с помощью @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.

Общие атрибуты

Обе аннотации используют следующие атрибуты:

Атрибут Описание

path

Путь к ресурсу шаблона Freemarker XML-дескриптора. Если не указан, используется соответствующий встроенный шаблон.

templateParams

JSON-объект с дополнительными параметрами, передаваемыми в шаблон. См. встроенные шаблоны для параметров, поддерживаемых встроенными шаблонами.

parentMenu

Идентификатор родительского пункта меню для генерируемого пункта меню. Если не указан, то пункт меню не создаётся. Если он указывает на отсутствующий пункт меню, автоматически создаётся новый корневой пункт меню.

viewId

Идентификатор генерируемого экрана.

viewRoute

Путь маршрута генерируемого экрана.

viewTitle

Заголовок страницы генерируемого экрана.

Значения по умолчанию

Когда необязательные атрибуты опущены, фреймворк выводит их из имени сущности <entityName>:

@ListViewTemplate @DetailViewTemplate

viewId

<entityName>.list

<entityName>.detail

viewTitle

<entityName> list

<entityName>

Маршрутизация

Маршрут разрешается из 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-дескриптор экрана.

В модели шаблона доступны следующие переменные:

  • entityMetaClassMetaClass аннотированной сущности;

  • viewTitle – заголовок экрана;

  • templateHelper – класс-помощник, который возвращает отфильтрованные свойства (getProperties) и свойства коллекций композиции (getCollectionProperties);

  • componentXmlFactory – фабрика, которая создаёт XML-фрагмент для компонента редактирования свойства;

  • все ключи из templateParams, включая includeProperties и excludeProperties.

В качестве отправной точки вы можете использовать встроенные шаблоны.

Если атрибут parentMenu аннотации @ListViewTemplate или @DetailViewTemplate задан, пункт меню, открывающий генерируемый экран, добавляется к указанному родительскому меню после загрузки стандартных XML-определений меню. Если parentMenu пуст, пункт меню не создаётся. Если он указывает на отсутствующий пункт меню, автоматически создаётся новый корневой пункт меню.