What are event actions
Event-responsive actions (a.k.a. event actions) are a type of integration components that are triggered in response to the occurrence of any event on a task form.
Event
There are two sources of events:
- task form - so called system events (e.g. changing the value of a variable, clicking on a button)
- event actions
Each event has a name and any number of properties of a defined type. The creator of an action can call any event from its action, on which any other action can be triggered. This solution gives more flexibility when creating processes compared to traditional form actions. The action creator has complete control over the timing and order of action execution.
The properties of an event can be mapped to action parameters in a manner analogous to the "context variables" of classic form actions.
Event actions vs. form actions
Form actions themselves define events in response to which they will be triggered. This way of triggering actions makes them less flexible (e.g. executing an action on another event requires a change in the implementation). In the case of event actions, it is the process creator who decides when the action should be triggered and with what parameters.
Order of execution of event actions
The diagram shows the order in which actions are executed in response to a change event. All 3 actions (Action1, Action2, Action3) are defined as follows (in the order shown):
Event | Action |
---|---|
change |
|
success (Action1) |
|
Actions are called in the order defined in the process map in depth, i.e. actions dependent on events of already called actions will be called first. Once this hierarchy is completed, the next action defined for the source event is called:
Asynchronous actions
Asynchronous actions are a special case, i.e. actions that perform any asynchronous action (e.g. http request) before the call is completed. In such a case, all defined actions are called even if some of them went into asynchronous mode. Only after the asynchronous call ends, the action chain is resumed.
System events
An overview of task form events for which event actions can be attached.
Global
Event | Description | Parameters |
---|---|---|
afterLoadForm | Event triggered when all form components are loaded |
|
afterAddDocument | Event raised after document has been attached to the process |
|
Process variables
A form variable that is not part of a dynamic table
Event | Description | Parameters |
---|---|---|
change | Event triggered when the value of a variable changes |
|
focus | Event triggered when a field receives focus | |
blur | Event triggered when field loses focus | |
show | Event triggered when a field is showed | |
hide | Event triggered when a field is hidden |
Tabels
Event | Description | Parameters |
---|---|---|
select | Event triggered when a row is selected in a table |
|
cellClick | Event triggered when clicking on a table cell |
|
afterAddRecords | Event triggered when records are added to a table |
|
afterRemoveRecords | Event triggered when records are removed from table |
|
afterAddRecords | Event triggered when records in table are changed |
|
afterRender | Event triggered after table is rendered | |
show | Event triggered after table discovery | |
hide | Event triggered when table is hidden |
Buttons
Buttons available on the form
Event | Description | Parameters |
---|---|---|
click | Event triggered when the button is clicked |
|
disable | Event triggered when button is locked | |
enable | Event triggered when button is unlocked | |
show | Event triggered when button is showed | |
hide | Event triggered when button is hidden |
Implementation
In a similar way to classic form actions, event actions are provided by plugins.
suncode-plugin.xml must contain workflow-components module declarations
< workflow-components key = "components" /> |
Action definition
Definition
@EventActions // indicate the component containing the event actions @ActionsScript( "scripts/eventactions.js" ) // indicate the script with the action implementation public class SetVariables { @Define public void setVariables(EventActionDefinitionBuilder eventAction ) { //@formatter:off eventAction .id( "test-action" ) .name( "Test action" ) .description( "eventaction.test-action.desc" ) .icon( SilkIconPack.APPLICATION_FORM_EDIT ) .category( Categories.GENERAL ) .documentationLink( "link" ) .parameter() // declaration of paremeters that the action takes .id( "param1" ) .name( "param1" ) .type( Types.STRING ) .create() .event("success") // declaration of events that can be triggered by actions .description("") .property("foo") // properties of events .name("Foo") .description("Bar") .type(Types.BOOLEAN) ; //@formatter:on } }
Registration of action implementation on the form
Registration of the action is done by calling
PW.EventActions.register(name, function)
where:
name: name of the registered action - it must match the name of the action from the component definition
function(context, ...params): function which will be called when an event occurs, to which the action is attached, it takes the following parameters:
context: the first parameter is the context of the form, used to effectively set the values of all form variables
<params>: subsequent parameters are the parameters for calling the action as declared, the order of the parameters follows the order in the component declaration
PW.EventActions.register("test-action", function(context, param1) { if(param1 == 'foo') { this.fireEvent("success", {"foo": true}); } });
Form context
The first parameter of each action is the context - the context of the form. It represents the current state of the form (values of all variables). This state is transferred to subsequent actions where it can be freely modified by them. When all actions are handled (within one form event), the final state is compared with the initial state of the form and all changes are applied to it.
Such a solution makes it very fast to change the value of form variables when the action is processed, because these changes are not propagated to the form at the moment of execution, but only when all actions from the so-called action chain are handled.
Changing values of variables
API reference: ContextAPI
Changing header variables:
PW.EventActions.register("test-action", function(context) { const value = context.variable("zmienna_1").get(); context.variable("zmienna_2").set("zmienna_1:" + value); });
Changing records in a table:
PW.EventActions.register("test-action", function(context) { const table = context.variableSet("table_1"); table.variable("zmienna_1").add("new_value"); });
Asynchronous actions
Asynchronous actions are actions in which there is a need to call asynchronous code (e.g. http requests or displaying a modal with a callback). In order for such an action to be able to change the state of the form and trigger further actions in response to events emitted through this.fireEvent() it is necessary to return control to the action framework.
This is done by the function this.async(function), which takes the function of the callback that will be called after returning from the asynchronous callback and returns the same function enriched with the logic for resuming the execution of the action.
Before calling the function transferred by the developer to the async() function, the system will load the current context of the form and resume execution of the action string, and when it is finished, it will apply the changes to the task form.
An example of correct and incorrect implementation of an asynchronous action:
PW.EventActions.register("async-action", function(context) { context.set("var1", "before"); // change of context is allowed before going into asynchronous mode this.fireEvent("before_async"); // same as event triggering // Asynchronous callback (e.g. server response, modal) // the function transferred as callback should be prepared in advance using this.async() function setTimeout(this.async(() => { context.set("var1", "after"); this.fireEvent("after_async"); }), 1000); // w przypadku gdy nie użyjemy funkcji this.async() - Accessing the context or and triggering events is not possible and will end in an error setTimeout(() => { this.fireEvent("will throw"); }, 1000); });
Context API
Context | |
---|---|
Function | Description |
variable(id) : VariableContext | Returns the VariableContext of the variable with the given identifier |
variableSet(tableId) : VariableSetContext | Returns the VariableSetContext of the table with the specified identifier |
VariableContext | |
---|---|
Function | Description |
set(newValue): this | Changing the value of a variable |
get(): Object | Returns the current value of the variable |
VariableSetContext | |
---|---|
Function | Description |
variable(id).add(newValue): this
| Adding a new value (row) |
variable(id).set(newValues): this | Changing the value of a variable in a table |
variable(id).get(): Object[] | Returns the current value of a variable (array) |
addRecords(tableStore): this | Adds records described by the specified TableStore |
Troubleshooting
Action call logging
If we need more information about the actions performed, their parameters or event properties, we can enable accurate logging at the Trace level. To do this, we need to:
- run the form in devMode (by adding &devMode=true to the end of the URL)
- make sure that the console displays messages at the Trace/Verbose (in Google Chrome the Verbose level is disabled by default
EventActions chain started: {name: 'afterLoadForm', props: constructor} Context {state: {…}} EventActions: invoking action: tr {action: {…}, on: {…}} on event: {name: 'afterLoadForm', props: constructor} Context {state: {…}} EventActions chain finished: {name: 'afterLoadForm', props: constructor} Context {state: {…}}
Known issues
EventAction: No active context found: most likely this code was invoked asynchronously, did you forgot 'this.async()'?
An error indicates an attempt to interact with an action, its context or parameters after the action has been executed or, in the case of asynchronous actions, without first using the this async() function. See Asynchronous actions
context.variableSet("t").updateRecordAt(1, {"var1": "value1", "var2": 2});