Jmix View

Любой экран Jmix может служить процессной формой. Это позволяет создавать более сложные взаимодействия с пользователем по сравнению с Input Dialog.

Создание процессной формы

Процессная форма Jmix — это обычный экран приложения, который включает аннотации для передачи деталей, относящихся к процессу.

Преобразование существующего экрана

Чтобы преобразовать обычный экран в процессную форму:

  • Добавьте аннотацию @ProcessForm в контроллер класса. Объявите результаты, параметры и выходные переменные по мере необходимости.

  • Инжектируйте и свяжите переменные процесса с помощью аннотации @ProcessVariable.

  • Обработайте нажатия кнопок для завершения задачи при помощи ProcessFormContext.

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

Использование Jmix Studio

Чтобы создать процессную форму с помощью Jmix Studio:

  1. Нажмите New → View, чтобы открыть Мастер создания экранов.

  2. Выберите шаблон BPM Process form.

    view wizard bpm process form

  3. Настройте шаблон формы и элемент, для которого он предназначен:

    • Выберите Form template из двух вариантов:

      • Process Form with Process Variables: эта форма отобразит компоненты для набора выбранных вами переменных.

      • Process Form for Entity Instance: эта форма отобразит компоненты для атрибутов конкретного экземпляра сущности вместе с любыми добавленными переменными процесса.

    • Выберите Form type:

      • User task form: эта форма предназначена для пользовательских задач.

      • Start form: эта форма предназначена для элементов стартового события.

        jmix view wizard 1

  4. В случае Process form for entity instance, укажите класс сущности, имя переменной и настройте фетч план.

  5. Укажите переменные процесса.

  6. В случае User task form укажите результаты формы. Значения по умолчанию — submit и reject. Измените их или добавьте новые.

  7. Добавьте локализуемые сообщения.

  8. Нажмите Create.

Форма появится среди других экранов в вашем приложении.

Настройка элемента с процессной формой

После создания процессной формы вы можете настроить элемент для её использования. Следуйте шагам:

  1. Откройте Modeler.

  2. Выберите соответствующий элемент (Пользовательская задача или Стартовое событие).

    user task

    form section

  3. Установите Form type в Jmix view.

  4. Установите Open mode:

    • Dialog: чтобы отображать форму в диалоге.

    • Navigate: чтобы отображать форму как экран с собственным URL.

  5. Укажите View id. Список опций будет включать все экраны приложения, аннотированные @ProcessForm. Модельер распознает процессную форму и заполнит оставшиеся настройки на основе её аннотаций.

  6. При наличии параметров формы, настройте их с соответствующим источником значений.

Процессная форма теперь настроена для использования в выбранном элементе.

@ProcessForm

Добавление @ProcessForm в класс контроллера экрана преобразует экран в процессную форму и делает её доступной для выбора в Modeler. Эта аннотация может включать следующие атрибуты:

  • Чтобы ограничить использование формы для определённых процессов, укажите ключи процессов в атрибуте allowedProcessKeys:

    @ProcessForm(allowedProcessKeys = {"process-1", "process-2"})
  • Чтобы объявить результаты формы, используйте outcomes:

    @ProcessForm(
            outcomes = { (1)
                    @Outcome(id = "approve"),
                    @Outcome(id = "reject")
            }
    )
  • Чтобы указать выходные переменные, используйте атрибут outputVariables:

    @ProcessForm(
            outputVariables = {
                    @OutputVariable(name = "order", type = Order.class),
                    @OutputVariable(name = "comment", type = String.class)
            }
    )
  • Чтобы установить переменную процесса только при завершении задачи с конкретным результатом, комбинируйте атрибуты outcomes и outputVariables следующим образом:

    @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 устанавливается во всех случаях.
  • Чтобы объявить параметры форму используйте атрибут params. Затем установите @ProcessFormParam на полях класса, чтобы связать их с этими параметрами.

    @ProcessForm(
            params = {
                    @Param(name = "nextActor"),
                    @Param(name = "entityPickerCaption")
            }
    )
    
        //...
    
        @ProcessFormParam
        private String nextActor;
    
        @ProcessFormParam
        private String entityPickerCaption;
    Для @ProcessFormParam может быть указан атрибут name. Если имя не указано явно, используется имя по умолчанию, основанное на названии поля.

@ProcessVariable

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

@ProcessVariable
private Date date;

@ViewComponent
@ProcessVariable(name = "order")
private EntityPicker<Order> orderEntityPicker;

Если name не указано явно, имя переменной процесса по умолчанию берётся из названия поля.

ProcessFormContext

Объект ProcessFormContext содержит информацию об определении процесса, который запускается, или о пользовательской задаче, которую необходимо завершить. Используйте ProcessFormContext, когда процессная форма открывается из Start Process или My Tasks. Чтобы программно открыть процессную форму с инжектированным ProcessFormContext используйте бин ProcessFormViews.

Рассмотрите пример запуска процесса:

@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 Закрытие текущего окна.

Примеры

Процессная Форма Запуска

Рассмотрим пример процессной формы, которая используется в качестве стартовой. Она содержит два поля:

  • Поле для ввода номера заказа.

  • Поле с элементом entityPicker для выбора менеджера. Менеджер может быть следующим актором процесса.

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(), вы можете определить переменные процесса напрямую.

Opening Forms Programmatically

Используйте сервис 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 Получите определение процесса по ключу.
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);
}

Процессная форма с параметрами

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

Параметры формы объявляются с помощью аннотации @ProcessForm и её атрибута params. Они связаны с полем класса с помощью аннотации @ProcessFormParam.

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

В общей сложности определим два параметра:

  • nextActor – для сохранения пользователя выбранного через EntityPicker для дальнейшего использования в процессе.

  • entityPickerCaption – для настройки заголовка компонента EntityPicker в зависимости от этапа процесса.

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 = "nextActor"),
                @Param(name = "entityPickerCaption")
        }
)
public class ActorSelectionForm extends StandardView {

    @Autowired
    private ProcessFormContext processFormContext;

    @ViewComponent
    private EntityPicker<String> userEntityPicker;


    //...

    @ProcessFormParam
    private String nextActor;

    @ProcessFormParam
    private String entityPickerCaption;

    @Subscribe
    private void onBeforeShow(BeforeShowEvent event) {
        userEntityPicker.setLabel(entityPickerCaption);
    }

    @Subscribe("completeTaskBtn")
    private void onCompleteTaskBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.taskCompletion()
                .addProcessVariable(nextActor, userEntityPicker.getValue())
                .complete();
        closeWithDefaultAction();
    }
}

Чтобы получить полный список параметров формы процесса, используйте объект ProcessFormContext:

List<FormParam> formParams = processFormContext.getFormData().getFormParams();