Jmix View Process Forms

The Input Dialog Forms forms discussed in the previous section are suitable for simple user interactions, such as data entry. However, Jmix allows you to use any views as process forms. This approach allows for process forms with complex layouts and behaviors tailored to your requirements.

To use a view as a process form, annotate it with @ProcessForm.

Avoid using @ProcessForm on views designed for other purposes. It is better to create a new view or extend an existing one.

Creating Jmix View Form

Select an element on the canvas. Then, in the BPMN Inspector panel, set the form type to Jmix view.

set jmix view type

Select an existing view from the dropdown list, or click the plus button button to create a new view.

create jmix view

In the View Creation Wizard, enter names for the view descriptor and controller, as well as other parameters:

jmix view wizard 1

Select a form template. Two options are available:

form template select
  • Process form with process variables (default) – Process variables of type Entity will be represented in the form using the EntityPicker component. The value of the variable can be selected from a dropdown list.

  • Process form for entity instance – Process variables of type Entity will be represented using the standard entity detail view. Users will be able to see all attributes of the entity.

When selecting Process form for entity instance, you may need to specify the entity class and configure the fetch plan:

form wizard entity variable
Some settings may be hidden if an existing variable is used (if the Use existing variable checkbox is checked).

After configuring the entity variable instance, the wizard prompts to add process variables. For example, to add the variable initiator:

add variable

When it is added, decide whether to display it in the form:

variable to form

Next, the wizard prompts to specify the outcomes of the form. By default, two outcomes are created: submit and reject. You can edit these and add more.

outcomes

Add a message for the form. When localizing the application into multiple languages, you will have the option to add messages for each supported language.

wizard messages

After completing the wizard, the controller file with the generated code will open:

@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();
    }
}

Process Variables

The @ProcessVariable annotation can be used to mark injected UI components or regular class fields. This annotation indicates that the value of the process variable will be written to this field when the process form is opened. In the case of a UI component, the variable’s value will be set in that component.

@ProcessVariable
private Date date;

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

The @ProcessVariable annotation includes an optional name attribute, which allows for the explicit specification of the name of the process variable associated with the field. If it is empty, then the process variable name matches the field name.

The ProcessFormContext.saveInjectedProcessVariables() methods enables saving values from annotated fields as process variables when starting the process or completing a user task.

ProcessFormContext

The ProcessFormContext object contains information about the definition of the process being started (when the process is started using a form) or the user task that needs to be completed.

Use ProcessFormContext when the process form is opened from the Start Process and My Tasks views. To open a process form with an injected ProcessFormContext programmatically, the bean ProcessFormViews should be used.

The ProcessFormContext object also contains methods for starting and completing the process.

Consider the following example of starting a process:

@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 Creates a ProcessStarting instance.
2 Sets a business key to the process instance.
3 Adds a process variable.
4 Starts the actual process.
5 Closes the opened window.

An example of completing a user task is provided below:

@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 Creates a TaskCompletion instance.
2 Sets a task outcome.
3 Indicates that values of class fields annotated with the @ProcessVariables should be collected and saved as process variables.
4 Completes the actual task.
5 Closes the opened window.

Declare Task Outcomes

In the BPM modeler, for the sequence flow element, you can define a condition by selecting a user task and its outcome from the drop-down list. To fill this list for a user task that uses a Jmix view process form, you can declare a list of possible outcomes in the form controller. Use the outcomes attribute of the @ProcessForm annotation for that.

@ProcessForm(
        outcomes = { (1)
                @Outcome(id = "approve"),
                @Outcome(id = "reject")
        }
)
public class OrderApprovalTaskForm extends StandardView {

Process Form Parameters

Jmix view process forms can accept external parameters defined in the modeler. The parameters used by the form are defined in the params attribute of the @ProcessForm annotation:

@ProcessForm(
        params = {
                @Param(name = "variableName"),
                @Param(name = "entityPickerCaption")
        }
)

These parameters are read by the BPMN Inspector, and you can see them after selecting the view.

form params

You can edit the parameters and provide a direct param value or use one of the existing process variables as a parameter value.

In the process form controller, use the @ProcessFormParam annotation on class fields to get parameter values.

@ProcessFormParam
private String variableName;

@ProcessFormParam
private String entityPickerCaption;

Another way to get a full list of process form parameters is to get them from the ProcessFormContext object:

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

As the @ProcessVariable annotation, the @ProcessFormParam supports an optional name attribute. If the attribute is not defined, then a field name is used as a parameter name.

See an example of a process form with parameters.

Output Variables

When you model the process, it may be useful to know which variables are set by the Jmix view process form in order to reuse them later in the process model. A way to achieve this is to use the outputVariabes attribute of the @ProcessForm annotation.

@ProcessForm(
        outputVariables = {
                @OutputVariable(name = "order", type = Order.class),
                @OutputVariable(name = "comment", type = String.class)
        }
)

Then, you will see the Output variable section in the BPMN Inspector panel. This section is read-only.

output variables

Often there are cases when a process variable is set only when the task is completed using a particular outcome. To declare this, place the outputVariables annotation attribute to the @Outcome annotation.

@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 The nextActor variable can be set when the task is completed with the approve outcome.
2 The rejectionReason variable can be set when the task is completed with the reject outcome.
3 The comment variable can be set in any case.

Output variables with the corresponding outcomes will be displayed.

output variables outcomes

Restrict Process Form Usage

By default, all process forms views are available within any process model. If you want to use some view in particular processes only, then you should specify processes keys in the allowedProcessKeys attribute of the @ProcessForm annotation.

@ProcessForm(allowedProcessKeys = {"process-1", "process-2"})

The form will be available only for processes with process-1 or process-2 process ids.

Opening Forms Programmatically

You can use the ProcessFormViews service to create start process forms and task process forms defined in the modeler.

In the example below, the start process form is opened by clicking the button in the browser view.

@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 Gets a process definition with the order-process key.
2 Shows the start process form with the received process definition.

The start process form can look like an example in the ProcessFormContext section.

To create a task form, use the openTaskProcessForm method:

@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);
}

Examples

Start Process Form

Let’s look at the example of the process form that is used as a start form. The form displays two fields:

  • A text field to enter the order number.

  • An entityPicker to select the manager. The manager can be the next process actor.

View XML descriptor:

<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>

View controller:

@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 The @ProcessForm annotation indicates that this view is a process form and the view will be available in the modeler.
2 We declare that the injected orderNumber UI component is a process variable. Since we develop a start process form, the variable has no value yet, but the annotation will be used on process start.
3 The same as 2, but here the manager process variable name differs from the managerEntityPicker field name.
4 ProcessFormContext is the object that we use to start the process.
5 When we start the process, we can pass an optional process instance business key. We use the orderNumber here.
6 The saveInjectedProcessVariables() indicates that values of the fields annotated with the @ProcessVariables should be saved as process variables on process start.

Instead of using saveInjectedProcessVariables() method you can explicitly set process variables:

@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
    processFormContext.processStarting()
            .withBusinessKey(orderNumber.getValue())
            .addProcessVariable("orderNumber", orderNumber.getValue())
            .addProcessVariable("manager",managerEntityPicker.getValue())
            .start();
    closeWithDefaultAction();
}

Task Process Form

Let’s look at the example of the task process form that displays two fields:

  • The first one will display a value of the existing process variable - orderNumber.

  • The second field will be used for the new process variable - comment.

Approve and Reject buttons complete the user task with the corresponding outcome.

View XML descriptor:

<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>

View controller:

@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 The form defines two possible outcomes that can be used in a sequence flow node condition in the modeler. This information is used by the modeler only.
2 The orderNumber variable has been already set on process start. Because of the @ProcessVariable annotation, the value of the orderNumber process variables will be set to the orderNumber text field when the form is displayed.
3 The comment variable is not set yet, but the @ProcessVariable annotation will be taken into account when we complete the task in the button click listener.
4 Values of all fields annotated with the @ProcessVariable will be saved as process variables on task completion.
5 An alternative way to define process variables. Instead of using the saveInjectedProcessVariables() method, you can define process variables directly.

Process Form with Parameters

Let’s assume that you need a form for the next process actor selection. The form should display EntityPicker field with users and save the result into a process variable. We want to use the form for selecting different actors at different process steps, so the form should have two parameters:

  • variableName

  • entityPickerCaption

View XML descriptor:

<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>

View controller:

@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();
    }
}