Jmix View Process Forms
Рассмотренные в предыдущем разделе Input Dialog Forms подходят для простых взаимодействий с пользователем, таких как ввод данных. Однако Jmix предоставляет возможность использовать любые экраны в качестве процессных форм. Этот подход позволяет добавить процессную форму со сложной компоновкой и поведением под ваши требования.
Чтобы экран можно было использовать в качестве процессной формы, добавьте на него аннотацию @ProcessForm
.
Старайтесь не использовать @ProcessForm на существующих экранах разработанных для других целей. Лучше создать новый экран или новый на основе существующего.
|
Создание формы на основе экрана Jmix
Выберите элемент на холсте. Затем, в панели BPMN Inspector в качестве типа формы установите Jmix view.

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

В диалоге View Creation Wizard введите названия для дескриптора и контроллера экрана, а также другие параметры:

Выберите шаблон формы. Доступны два варианта:

-
Process form with process variables (default) – переменные процесса c типом Entity будут представлены в форме компонентом EntityPicker. Значение переменной можно будет выбрать из выпадающего списка.
-
Process form for entity instance – переменные процесса с типом Entity будут представлены стандартным entity detail view. Пользователи смогут увидеть все атрибуты сущности.
При выборе Process form for entity instance может потребоваться указать класс сущности и настроить фетч-план:

Некоторые настройки могут быть скрыты если используется существующая переменная. (если отмечен чекбокс Use existing variable.) |
После завершения настройки экземпляра переменной сущности мастер предложит добавить переменные процесса. Например, добавим переменную initiator
:

Вы можете решить, показывать ли ее в форме:

Далее мастер предложит указать исходы формы. По умолчанию создаются два исхода: submit
и reject
. Вы можете редактировать их или добавлять новые.

Наконец, добавьте сообщение, для формы. При локализации приложения на несколько языков, будет возможность добавить сообщения для каждого из поддерживаемых языков.

После завершения работы мастера откроется файл контроллера со сгенерированным кодом:
@ProcessForm(outcomes = {
@Outcome(id = "submit"),
@Outcome(id = "reject")
}, outputVariables = {
@OutputVariable(name = "initiator", type = User.class),
@OutputVariable(name = "orderVar", type = Order.class)
})
@Route(value = "order-approval-form", layout = MainView.class)
@ViewController("smpl_OrderApprovalForm")
@ViewDescriptor("order-approval-form.xml")
public class OrderApprovalForm extends StandardView {
@Autowired
private ProcessFormContext processFormContext;
@ProcessVariable(name = "initiator")
@ViewComponent
private EntityPicker<User> initiatorField;
@ProcessVariable(name = "orderVar")
private Order orderVar;
@ViewComponent
DataContext dataContext;
@ViewComponent
private InstanceContainer<Order> orderDc;
@Subscribe
public void onBeforeShow(final BeforeShowEvent event) {
if (orderVar == null) {
orderVar = dataContext.create(Order.class);
}
orderDc.setItem(dataContext.merge(orderVar));
}
@Subscribe(id = "submitBtn", subject = "clickListener")
protected void onSubmitBtnClick(ClickEvent<JmixButton> event) {
dataContext.save();
processFormContext.taskCompletion()
.withOutcome("submit")
.saveInjectedProcessVariables()
.complete();
closeWithDefaultAction();
}
@Subscribe(id = "rejectBtn", subject = "clickListener")
protected void onRejectBtnClick(ClickEvent<JmixButton> event) {
dataContext.save();
processFormContext.taskCompletion()
.withOutcome("reject")
.saveInjectedProcessVariables()
.complete();
closeWithDefaultAction();
}
}
Переменные Процесса
Аннотацией @ProcessVariable
можно помечать инжектированные компоненты UI или обычные поля класса. Она указывает, что значение переменной процесса будет записано в это поле при открытии процессной формы. В случае компонента UI значение переменной будет установлено в этот компонент.
@ProcessVariable
private Date date;
@ViewComponent
@ProcessVariable(name = "order")
private EntityPicker<Order> orderEntityPicker;
Аннотация @ProcessVariable
имеет опциональный атрибут name
с помощью которого можно явно указать имя переменной процесса с которой связано поле. Если атрибут name
не указан, предполагается, что имя переменной процесса совпадает с именем поля.
Метод ProcessFormContext.saveInjectedProcessVariables() позволяет сохранять значения аннотированных полей в качестве переменных процесса при запуске процесса или при завершении задачи пользователя.
|
ProcessFormContext
Объект ProcessFormContext
содержит информацию об определении запускаемого процесса (когда для запуска процесса используется форма) или пользовательской задачи, которую необходимо выполнить.
ProcessFormContext
можно использовать, если процессная форма открыть из экранов Start process и My Tasks. Чтобы открыть процессную форму с инжектированным ProcessFormContext
программно, используйте бин ProcessFormViews.
Объект ProcessFormContext
также содержит методы для запуска процесса и завершения процесса.
Рассмотрите пример запуска процесса:
@ProcessVariable
private Date date;
@ViewComponent
@ProcessVariable(name = "order")
private EntityPicker<Order> orderEntityPicker;
@Autowired
private ProcessFormContext processFormContext;
@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
processFormContext.processStarting() (1)
.withBusinessKey("order-1") (2)
.addProcessVariable("date", date)
.addProcessVariable("order", orderEntityPicker.getValue()) (3)
.start(); (4)
closeWithDefaultAction(); (5)
}
1 | Создание экземпляра ProcessStarting . |
2 | Добавление бизнес-ключа для экземпляра процесса. |
3 | Добавление переменной процесса. |
4 | Запуск процесса. |
5 | Закрытие текущего окна. |
Пример завершения пользовательской задачи:
@Autowired
private ProcessFormContext processFormContext;
@Subscribe("rejectBtn")
protected void onRejectBtnClick(ClickEvent<JmixButton> event) {
processFormContext.taskCompletion() (1)
.withOutcome("reject") (2)
.saveInjectedProcessVariables() (3)
.complete(); (4)
closeWithDefaultAction(); (5)
}
1 | Создание экземпляра TaskCompletion . |
2 | Выбор исхода для задачи. |
3 | Указывает, что значения полей класса, аннотированных @ProcessVariable , должны быть собраны и сохранены в качестве переменных процесса. |
4 | Завершение задачи. |
5 | Закрытие текущего окна. |
Указание Исходов Задачи
В моделере можно определить условие для элемента sequence flow выбрав пользовательскую задачу и её исход из выпадающего списка. Варианты исходов будут доступны в выпадающем списке. Чтобы заполнить этот список, необходимо объявить возможные исходы в контроллере формы. Для этого используйте аннотацию @ProcessForm
с аттрибутом outcomes
.
@ProcessForm(
outcomes = { (1)
@Outcome(id = "approve"),
@Outcome(id = "reject")
}
)
public class OrderApprovalTaskForm extends StandardView {
Параметры Процессных Форм
Процессные формы экранов Jmix могут принимать внешние параметры, определенные в моделере. Параметры, используемые формой, определяются в атрибуте params
аннотации @ProcessForm
:
@ProcessForm(
params = {
@Param(name = "variableName"),
@Param(name = "entityPickerCaption")
}
)
Эти параметры считываются моделером, и их можно увидеть после выбора экрана.

Параметры можно редактировать, можно задать значение напрямую или использовать в качестве значения одну из существующих переменных процесса.
Внутри контроллера процессной формы используйте аннотацию @ProcessFormParam
на полях класса для получения значений параметров.
@ProcessFormParam
private String variableName;
@ProcessFormParam
private String entityPickerCaption;
Полный список параметров процессной формы также можно получить из объекта ProcessFormContext
:
List<FormParam> formParams = processFormContext.getFormData().getFormParams();
Как и аннотация @ProcessVariable
, аннотация @ProcessFormParam
также имеет необязательный атрибут name
. Если этот атрибут не указан, то в качестве имени параметра будет использовано имя поля.
Ниже привёден пример процессной формы с параметрами.
Выходные Переменные
При моделировании процесса полезно знать, какие переменные задаются процессной формой экрана Jmix, чтобы впоследствии использовать их повторно в модели процесса. Для обозначения используйте атрибут outputVariables
аннотации @ProcessForm
.
@ProcessForm(
outputVariables = {
@OutputVariable(name = "order", type = Order.class),
@OutputVariable(name = "comment", type = String.class)
}
)
После этого в панели BPMN Inspector появится раздел Output variable. Этот раздел доступен только для чтения.

Существуют ситуации, когда переменная процесса задается только при выполнении задачи с определенным выходом. Чтобы это обозначить, поместите аттрибут outputVariables
а аннотацию @Outcome
.
@ProcessForm(
outcomes = {
@Outcome(
id = "approve",
outputVariables = {
@OutputVariable(name = "nextActor", type = User.class) (1)
}
),
@Outcome(
id = "reject",
outputVariables = {
@OutputVariable(name = "rejectionReason", type = String.class) (2)
}
)
},
outputVariables = {
@OutputVariable(name = "comment", type = String.class) (3)
}
)
1 | Переменная nextActor будет задана когда задача завершится с исходом approve . |
2 | Переменная rejectionReason будет задана когда задача завершится с выходом reject . |
3 | Переменная comment будет задана в любом из случаев. |
Информация о выходных переменных отображается в соответствующем разделе панели свойств.

Ограничения использования процессных форм
По умолчанию все процессные формы экранов доступны в рамках любой модели процесса. Если вы хотите использовать какой-либо экран только в определенных процессах, нужно указать ключи процессов в атрибуте allowedProcessKeys
аннотации @ProcessForm
.
@ProcessForm(allowedProcessKeys = {"process-1", "process-2"})
Форма будет доступна только для процессов с идентификаторами process-1
и process-2
в моделере.
Программное открытие форм
Сервис ProcessFormViews
позволяет программно создавать формы запуска процесса и пользовательских задач определенных в моделере.
В приведенном ниже примере процессная форма запуска открывается нажатием кнопки на экране браузера.
@Autowired
private RepositoryService repositoryService;
@Autowired
protected ProcessFormViews processFormViews;
@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(final ClickEvent<JmixButton> event) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() (1)
.processDefinitionKey("order-process")
.latestVersion()
.singleResult();
processFormViews.openStartProcessForm(processDefinition, this); (2)
}
1 | Получение определения процесса по ключу order-process . |
2 | Отображение процессной формы. |
Чтобы создать форму для пользовательской задачи, используйте метод openTaskProcessForm
:
@Autowired
private TaskService taskService;
@Autowired
private ProcessFormViews processFormViews;
@Subscribe("openTaskBtn")
public void onOpenTaskBtnClick(ClickEvent<JmixButton> event) {
Task task = taskService.createTaskQuery()
.processDefinitionKey("approve-order-process")
.taskAssignee("admin")
.active()
.orderByTaskCreateTime()
.list()
.get(0);
processFormViews.openTaskProcessForm(task, this);
}
Примеры
Процессная Форма Запуска
Давайте рассмотрим пример процессной формы, которая используется в качестве стартовой. Она содержит два поля:
-
текстовое поле для ввода номера заказа.
-
выпадающий список пользователей для выбора менеджера. Менеджер может быть следующим актором процесса.
XML дескриптор:
<view xmlns="http://jmix.io/schema/flowui/view"
title="msg://com.company.bpmex1.view.forms/startProcessForm.title">
<layout>
<formLayout>
<textField id="orderNumber"
label="msg://com.company.bpmex1.view.forms/orderNumber"
datatype="string"/>
<entityPicker id="managerEntityPicker"
metaClass="User"
label="msg://managerEntityPicker.caption">
<actions>
<action id="lookup" type="entity_lookup"/>
<action id="clear" type="entity_clear"/>
</actions>
</entityPicker>
</formLayout>
<hbox id="actionsPanel" spacing="true">
<button id="startProcessBtn"
icon="CHECK"
text="msg://com.company.bpmex1.view.forms/startProcessBtn.text"/>
</hbox>
</layout>
</view>
Контроллер экрана:
@ViewController("OrderApprovalStartForm")
@ViewDescriptor("order-approval-start-form.xml")
@ProcessForm (1)
public class OrderApprovalStartForm extends StandardView {
@ViewComponent
@ProcessVariable (2)
private TypedTextField<String> orderNumber;
@ViewComponent
@ProcessVariable(name = "manager") (3)
private EntityPicker<User> managerEntityPicker;
@Autowired
private ProcessFormContext processFormContext; (4)
@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
processFormContext.processStarting()
.withBusinessKey(orderNumber.getValue()) (5)
.saveInjectedProcessVariables() (6)
.start();
closeWithDefaultAction();
}
}
1 | Аннотация @ProcessForm указывает, что этот экран является процессной формой и будет доступен в моделере. |
2 | Аннотация @ProcessVariable указывает, что компонент orderNumber соответствует процессной переменной orderNumber . Поскольку форма является стартовой, переменная еще не задана, но аннотация будет использоваться при запуске процесса. |
3 | Аннотация @ProcessVariable указывает, что компонент managerEntityPicker соответствует процессной переменной manager . Имя процессной переменной manager указано явно, так как оно отличается от имени компонента. |
4 | ProcessFormContext это объект для запуска процесса. |
5 | При запуске процесса можно передать бизнес-ключ для экземпляра. В данном случае используется orderNumber . |
6 | Метод saveInjectedProcessVariables() указывает, что значения полей, аннотированных @ProcessVariable , должны быть сохранены в качестве переменных процесса при запуске процесса. |
Вместо использования метода saveInjectedProcessVariables()
можно задать переменные процесса явно:
@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
processFormContext.processStarting()
.withBusinessKey(orderNumber.getValue())
.addProcessVariable("orderNumber", orderNumber.getValue())
.addProcessVariable("manager",managerEntityPicker.getValue())
.start();
closeWithDefaultAction();
}
Процессная форма задачи
Рассмотрим пример процессной формы задачи с двумя полями:
-
Первое поле соответствует существующей переменной процесса –
orderNumber
. -
Второе поле будет использоваться для новой переменной процесса -
comment
.
Кнопки Approve и Reject завершают пользовательскую задачу с соответствующим исходом.
XML-дескриптор экрана:
<view xmlns="http://jmix.io/schema/flowui/view"
title="msg://orderApprovalTaskForm.title">
<layout>
<formLayout>
<textField id="orderNumber" readOnly="true"
label="msg://orderNumber"/>
<textField id="commentField" label="msg://comment"/>
</formLayout>
<hbox id="actionsPanel" spacing="true">
<button id="approveBtn" icon="CHECK" text="msg://approveBtn.text"/>
<button id="rejectBtn" icon="BAN" text="msg://rejectBtn.text"/>
</hbox>
</layout>
</view>
Контроллер экрана:
@ViewController("OrderApprovalTaskForm")
@ViewDescriptor("order-approval-task-form.xml")
@ProcessForm(
outcomes = { (1)
@Outcome(id = "approve"),
@Outcome(id = "reject")
}
)
public class OrderApprovalTaskForm extends StandardView {
@ViewComponent
@ProcessVariable (2)
private TypedTextField<String> orderNumber;
@ViewComponent
@ProcessVariable(name = "comment") (3)
private TypedTextField<String> commentField;
@Autowired
private ProcessFormContext processFormContext;
@Subscribe("approveBtn")
protected void onApproveBtnClick(ClickEvent<JmixButton> event) {
processFormContext.taskCompletion()
.withOutcome("approve")
.saveInjectedProcessVariables() (4)
.complete();
closeWithDefaultAction();
}
@Subscribe("rejectBtn")
protected void onRejectBtnClick(ClickEvent<JmixButton> event) {
processFormContext.taskCompletion()
.withOutcome("reject")
.addProcessVariable("comment", commentField.getValue()) (5)
.complete();
closeWithDefaultAction();
}
}
1 | Форма определяет два возможных исхода. |
2 | Переменная orderNumber уже установлена при запуске процесса. Благодаря аннотации @ProcessVariable значение переменной процесса orderNumber будет отображено в текстовом поле orderNumber при появлении формы. |
3 | Переменная comment еще не установлена, но аннотация @ProcessVariable будет учтена при завершении задачи в слушателе нажатия кнопки. |
4 | Значения всех полей с аннотацией @ProcessVariable будут сохранены как переменные процесса по завершении задачи. |
5 | Вместо того, что бы использовать метод saveInjectedProcessVariables() , вы можете определить переменные процесса напрямую. |
Процессная форма с параметрами
Предположим, требуется форма для выбора следующего актора процесса. Форма должна содержать поле EntityPicker с пользователями и сохранять результат в переменной процесса. Поскольку форма будет использоваться для выбора различных акторов на разных этапах процесса, она должна иметь два параметра:
-
variableName
-
entityPickerCaption
XML-дескриптор экрана:
<view xmlns="http://jmix.io/schema/flowui/view"
title="msg://com.company.bpmex1.view.forms/actorSelectionForm.title">
<layout spacing="true">
<formLayout width="20em">
<entityPicker id="userEntityPicker"
metaClass="User"
property="username">
<actions>
<action id="lookup" type="entity_lookup"/>
<action id="clear" type="entity_clear"/>
</actions>
</entityPicker>
</formLayout>
<hbox spacing="true">
<button id="completeTaskBtn" icon="CHECK" text="msg://completeTask"/>
</hbox>
</layout>
</view>
Контроллер экрана:
@ProcessForm(
params = {
@Param(name = "variableName"),
@Param(name = "entityPickerCaption")
}
)
public class ActorSelectionForm extends StandardView {
@Autowired
private ProcessFormContext processFormContext;
@ViewComponent
private EntityPicker<String> userEntityPicker;
@ProcessFormParam
private String variableName;
@ProcessFormParam
private String entityPickerCaption;
@Subscribe
private void onBeforeShow(BeforeShowEvent event) {
userEntityPicker.setLabel(entityPickerCaption);
}
@Subscribe("completeTaskBtn")
private void onCompleteTaskBtnClick(ClickEvent<JmixButton> event) {
processFormContext.taskCompletion()
.addProcessVariable(variableName, userEntityPicker.getValue())
.complete();
closeWithDefaultAction();
}
}