Czym są akcje zdarzeniowe

Akcje reagujące na zdarzenia (inaczej akcje zdarzeniowe) to rodzaj komponentów integracyjnych które uruchamiane są w odpowiedzi na wystąpienie dowolnego zdarzenia na formularzu zadania.

Zdarzenie

Istnieją dwa źródła zdarzeń:

  1. formularz zadań - tzw. zdarzenia systemowe (np. zmiana wartości zmiennej, kliknięcie w przycisk)
  2. akcje zdarzeniowe

Każde zdarzenie posiada nazwę oraz dowolną liczbę właściwości (properties) o zdefiniowanym typie. Twórca akcji może ze swojej akcji wywołać dowolne zdarzenie, na które można będzie uruchomić dowolną inną akcje. Takie rozwiązanie daje więcej elastyczności podczas tworzenia procesów w stosunku do tradycyjnych akcji formularza. Twórca akcji ma całkowitą kontrolę nad momentem i kolejnością wykonywania akcji.

Właściwości zdarzenia mogą być mapowane na parametry akcji w sposób analogiczny do "zmiennych kontekstowych" klasycznych akcji formularza.

Akcje zdarzeniowe a akcje formularza

Akcje formularza same definiują zdarzenia w odpowiedzi na które zostaną uruchomione. Taki sposób uruchamiania akcji sprawia że nie są one tak elastyczne (np. wykonanie akcji na innym zdarzeniu wymaga zmiany implementacji). W przypadku akcji zdarzeniowych, to twórca procesu decyduje kiedy dana akcja ma się wywołać i z jakimi parametrami.

Kolejność wykonywania akcji zdarzeniowych

Diagram przedstawia kolejność wykonywania akcji w reakcji na zdarzenie change. Wszystkie 3 akcje (Action1, Action2, Action3) zdefiniowane są następujący sposób (w podanej kolejności):

ZdarzenieAkcje
change
  1. Action1
  2. Action3
success (Action1)
  1. Action2

Akcje są wywoływane w kolejności zdefiniowanej na mapie procesu w głąb czyli najpierw wywołane będą akcje zależne od zdarzeń już wywołanych akcji, dopiero po zakończeniu tego łańcucha wywoływana jest następna akcja zdefiniowana dla zdarzenia źródłowego:

sequenceDiagram
autonumber
participant Form
participant EventsController
participant Action1
participant Action2
participant Action3

Form->>EventsController: event:change
activate EventsController
EventsController->>Form: restore()
deactivate EventsController
activate Form
Form->>EventsController: return context:1
deactivate Form
 
activate EventsController
EventsController->>Action1: on(change): action1(context:1)
deactivate EventsController

activate Action1
Action1-->>EventsController: fireEvent("success")
Action1->>EventsController: return context:2
deactivate Action1

activate EventsController
EventsController->>Action3: on(action1_success): action3(context:2)
deactivate EventsController
activate Action3
Action3->>EventsController: return context:3
deactivate Action3

activate EventsController
EventsController->>Action2: on(change): action2(context:3)
deactivate EventsController
activate Action2
Action2->>EventsController: return context:4
deactivate Action2

activate EventsController
EventsController->>Form: apply(context:4)
deactivate EventsController

Akcje asynchroniczne

Szczególnym przypadkiem są akcje asynchroniczne czyli takie które przed zakończeniem wywołania wykonują dowolną asynchroniczną akcje (np. żądanie http). W takim przypadku wywoływane są wszystkie zdefiniowane akcje nawet jeżeli niektóre z nich przeszły w tryb asynchroniczny. Dopiero po zakończeniu wywłania asynchronicznego łąńcuch akcji jest wznawiany.

sequenceDiagram
autonumber
participant Form
participant EventsController
participant Async1
participant Sync1

Form->>EventsController: event:change
activate EventsController
EventsController->>Form: restore()
deactivate EventsController
activate Form
Form->>EventsController: return context:1
deactivate Form

activate EventsController
EventsController->>Async1: on(change): async1(context:1)
deactivate EventsController

activate Async1
Async1-->>EventsController: fireEvent("before_async")
Async1-->>EventsController: start async(context:2)
deactivate Async1

activate EventsController
EventsController->>Sync1: on(async1_before_async): sync1(context:2)
deactivate EventsController

activate Sync1
Sync1->>EventsController: return context:3
deactivate Sync1

activate EventsController
EventsController->>Form: apply(context:3)
deactivate EventsController

Async1 --> Async1: async done
Async1 ->> EventsController: notify()
activate EventsController
EventsController->>Form: restore()
deactivate EventsController
activate Form
Form->>EventsController: return context:4
deactivate Form
activate EventsController
EventsController->>Async1: resume(context:4)
deactivate EventsController

activate Async1
Async1-->>EventsController: fireEvent("after_async")
Async1->>EventsController: return context:5
deactivate Async1
activate EventsController
EventsController->>Sync1: on(async1_after_async): sync1(context:5)
deactivate EventsController

activate Sync1
Sync1->>EventsController: return context:6
deactivate Sync1

activate EventsController
EventsController->>Form: apply(context:6)
deactivate EventsController

 

Zdarzenia systemowe

Spis zdarzeń formularza zadania dla których mogą zostać podpięte akcje zdarzeniowe.

Globalne

ZdarzenieOpisParametry
afterLoadFormZdarzenie wywoływane po załadowaniu wszystkich komponentów formularza
  • previousUrl: String
    Adres URL strony z której nastąpiło przejście na formularz zadania
afterAddDocumentZdarzenie wywoływane po dodaniu dokumentu do procesu
  • documentId: String
    Identyfikator dokumentu podłączonego do procesu
  • fileId: Integer
    Identyfikator pliku podłączonego do procesu
  • versionNo: Integer
    Numer wersji pliku
  • docClassId: Integer
    Identyfikator docelowej klasy dokumentów
  • docClassName: String
    Nazwa docelowej klasy dokumentów

Zmienne procesu

Zmienna formularza, nie będąca częścią tabelki dynamicznej

ZdarzenieOpisParametry
changeZdarzenie wywoływane po zmianie wartości zmiennej
  • oldValue: String
    Wartość pola przed zmianą

    Wartość tekstowa niezależnie od typu zmiennej - spowodowane jest to ograniczeniami związanymi z systemem typów i braku typów generycznych

focusZdarzenie wywoływane gdy pole otrzyma focus 
blurZdarzenie wywoływane gdy pole straci focus 
showZdarzenie wywoływane gdy pole zostanie odkryte 
hideZdarzenie wywoływane gdy pole zostanie ukryte 

Tabelki

ZdarzenieOpisParametry
selectZdarzenie wywoływane po wybraniu wiersza w tabeli
  • tableId: String
    Identyfikator tabeli dynamicznej

  • rowNo: Integer
    Numer zaznaczonego wiersza tabeli
cellClickZdarzenie wywoływane po kliknięciu w komórkę tabeli
  • tableId: String
    Identyfikator tabeli dynamicznej

  • rowNo: Integer
    Numer zaznaczonego wiersza tabeli
  • fieldId: String
    Identyfikator zmiennej klikniętej komórki
afterAddRecordsZdarzenie wywoływane po dodaniu rekordów do tabeli
  • tableId: String
    Identyfikator tabeli dynamicznej

  • newRecords: TableStore
    Dodane rekordy w formacie TableStore
afterRemoveRecordsZdarzenie wywoływane po usunięciu rekordów z tabeli
  • tableId: String
    Identyfikator tabeli dynamicznej

  • deletedRecords: TableStore
    Usunięte rekordy w formacie TableStore
afterChangeRecordsZdarzenie wywoływane po zmianie rekordów w tabeli
  • tableId: String
    Identyfikator tabeli dynamicznej

  • changedRecords: TableStore
    Zmienione rekordy w formacie TableStore
afterRenderZdarzenie wywołane po wyrenderowaniu tabeli 
showZdarzenie wywołane po odkryciu tabeli 
hideZdarzenie wywołane po ukryciu tabeli 

Przyciski

Przyciski dostępne na formularzu 

ZdarzenieOpisParametry
clickZdarzenie wywoływane po kliknięciu przycisku

 

 

 

disableZdarzenie wywoływane po zablokowaniu przycisku 
enableZdarzenie wywoływane po odblokowaniu przycisku 
showZdarzenie wywoływane po odkryciu przycisku 
hideZdarzenie wywoływane po ukryciu przycisku 

Implementacja

W sposób analogiczny do klasycznych akcji formularza, akcje zdarzeniowe dostarczane są przez wtyczki.

suncode-plugin.xml musi zawierać deklaracje modułu workflow-components

 

<workflow-components key="components" />

Definicja akcji

Definicja 

@EventActions // oznaczenie komponentu zawierającego akcje zdarzeniowe
@ActionsScript( "scripts/eventactions.js" ) // wskazuje skrypt z implementacją akcji
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() // deklaracja paremetrów które przyjmuje akcja
                .id( "param1" )
                .name( "param1" )
                .type( Types.STRING )
                .create()
            .event("success") // deklaracja zdarzeń które mogą być wyzwolone przez akcje
                .description("")
                .property("foo") // właściwości zdarzeń
                    .name("Foo")
                    .description("Bar")
                    .type(Types.BOOLEAN)
        ;
        //@formatter:on
    }
}

Rejestracja implementacji akcji na formularzu

Rejestracja akcji odbywa się poprzez wywołanie 

    PW.EventActions.register(name, function)

gdzie:

name: nazwa rejestrowanej akcji - musi być zgodna z nazwą akcji z definicji komponentu

function(context, ...params): funkcja która zostanie wywołana w momencie wystąpienia zdarzenia, pod które została podpięta dana akcja, przyjmuje ona następujące parametry:

context: pierwszy parametr to kontekst formularza, służący do efektywnego ustawiania wartości wszystich zmiennych formularza

<params>: kolejne parametry to parametry wywołania akcji zgodnie z deklaracją, kolejność parametrów jest zgodna z kolejnością w deklaracji komponentu 

PW.EventActions.register("test-action", function(context, param1) {
    if(param1 == 'foo') {
        this.fireEvent("success", {"foo": true});
    }
});

Kontekst formularza

Pierwszym parametrem każdej akcji jest context - kontekst formularza. Reprezentuje on aktualny stan formularza (wartości wszystkich zmiennych). Stan ten jest przekazywany do kolejnych akcji gdzie może być przez nie dowolnie modyfikowany. W momencie obsłużenia wszystkich akcji (w ramach jednego zdarzenia formularza), finalny stan porównywany jest z początkowym stanem formularza a wszystkie zmiany są na niego aplikowane. 

Takie rozwiązanie powoduje że zmiana wartości zmiennych formularza w momencie przetwarzania akcji jest bardzo szyba, ponieważ zmiany te nie są propagowane na formularz w momencie dokonania, a dopiero w momencie gdy wszystkie akcje z tzw. łańcucha akcji zostaną obsłużone.

Zmiana wartości zmiennych

API reference: Akcje reagujące na zdarzenia

Zmiana zmiennych nagłówkowych:

PW.EventActions.register("test-action", function(context) {
    const value = context.variable("zmienna_1").get();
    context.variable("zmienna_2").set("zmienna_1:" + value);
});

Zmiana rekordów w tabeli:

PW.EventActions.register("test-action", function(context) {
    const table = context.variableSet("table_1");
    table.variable("zmienna_1").add("new_value");
});

Hooks

Hook'i pozwalają na uruchomienie funkcji w momencie zajścia danego zdarzenia związanego z kontekstem formularza. Zarejestrowane funkcje wykonywane są w kolejności rejestracji.

afterFlush

Hook afterFlush uruchamiany jest po zaaplikowaniu wszystkich zmian z kontekstu na formularz zadania. Przydatne np. w przypadku gdy używamy API które nie jest świadome kontekstu i wymaga aby wartości znalazły się już na formularzu.

Przykładem takiego scenariusza jest zapis formularza - formularz zapisany zostanie jak wszystkie akcje związane z danym zdarzeniem zostaną wykonane.

 context.afterFlush(() => FormService.save());

 

 

Zmienne kontekstowe

Zmienne kontekstowe dostępne są tylko w parametrach typu function

 

Deklaracja zmiennych kontekstowych w komponencie:

<...>
eventAction
    .id( "test-action" )
    <...>
    .parameter() // deklaracja paremetrów które przyjmuje akcja
        .id( "param1" )
        .name( "param1" )
        .type( Types.FUNCTION )
        .create()
    .contextVariable()
    	.id("context-var1")
    	.name("context-var1")
    	.type(Types.STRING)
    	.create()
    .contextVariable()
    	.id("context-var2")
    	.name("context-var2")
    	.type(Types.INTEGER)
    	.create()

 

W celu przekazania wartości zmiennych kontekstowych po stronie akcji należy podczas wywołania funkcji (zdefiniowanej jako parametr) dostarczyć wszystkie zdefiniowane zmienne kontekstowe:

// param1 - parametr typu function
// param1.call(<context>) - do metody call() należy przekazać wszystkie zdefinowane zmienne wraz z wartościami
PW.EventActions.register("test-action", function(context, param1) {
    param1.call({"context-var1": "value1", "context-var2": null});
});

 

Akcje asynchroniczne

Akcje asynchroniczne to akcje w których zachodzi potrzeba wywołania asynchronicznego kodu (np. żądania http lub wyświetlenie modal'a z callbackiem). Aby taka akcja była w stanie zmieniać stan formularza i wyzwalać kolejne akcje w reakcji na zdarzenia wyemitowany poprzez  this.fireEvent() niezbędne jest zwrócenie kontroli do framework'u akcji.

Służy do tego funkcja this.async(function) która przyjmuje funkcje callback'u jaki będzie wywołany po powrocie z asynchronicznego wywołania oraz zwraca tą samą funkcję wzbogaconą o logikę wznowienia wykonywania akcji.

Przed wywołaniem funkcji przekazanej przez twórcę do funkcji async() system wczyta aktualny kontekst formularza i wznowi wykonywanie łańcucha akcji, a po zakończeniu naniesie zmiany na formularz zadania.

Przykład poprawnej i błędnej implementacji akcji asynchronicznej:

PW.EventActions.register("async-action", function(context) {
    context.set("var1", "before"); // zmiana kontekstu jest dozwolona przed przejściem w tryb asynchroniczny
	this.fireEvent("before_async"); // tak samo jak wyzwalanie zdarzeń
	
    // wywołanie asynchroniczne (np. odpowiedź serwera, modala)
    // funkcje przekazywaną jako callback należy wcześniej przygotować używając funkcji this.async()
    setTimeout(this.async(() => {
            context.set("var1", "after");
            this.fireEvent("after_async");
    }), 1000);
 
    // w przypadku gdy nie użyjemy funkcji this.async() - dostęp do kontekstu bądź i wyzwalanie zdarzeń nie jest możliwe i zakończy się błędem
    setTimeout(() => {
            this.fireEvent("will throw");
    }, 1000);
});

Context API

 

Context
FunkcjaOpis

variable(id) : VariableContext

Zwraca VariableContext zmiennej o podanym identyfikatorze.

Wsparcie dla zmiennych tabelarycznych jest ograniczone - a w przyszłości zostanie usunięte.

variableSet(tableId) : VariableSetContext

Zwraca VariableSetContext tabeli o podanym identyfikatorze

afterFlush(hookFn)

Rejestruje afterFlush hook.

 context.afterFlush(() => FormService.save());

 

 

VariableContext
FunkcjaOpis

set(newValue): this

Zmiana wartości zmiennej
get(): ObjectZwraca aktualną wartość zmiennej
VariableSetContext
FunkcjaOpisPrzykład

variable(id).add(newValue): this

Dodanie nowej wartości (wiersza)

Deprecated

 
variable(id).set(newValues): this

Zmiana wartości zmiennej w tabeli

Deprecated

 
variable(id).get(): Object[]Zwraca aktualną wartość zmiennej (tablica) 
addRecords(tableStore): thisDodaje rekordy opisane podanym TableStore'em 

deleteRecords(tableStore): this

 Usuwa rekordy, dla których wszystkie pola są takie same jak pola dowolnego wiersz z wejściowej tabelki

 

 

deleteRecord(index): this Usuwa rekord o podanym indeksie 
updateRecordAt(index, data): this Aktualizuje rekord o podanym indeksie
 context.variableSet("t").updateRecordAt(1, {"var1": "value1", "var2": 2});

Troubleshooting

Logowanie wywołania akcji

Jeżeli potrzebujemy więcej informacji o wykonywanych akcjach, ich parametrach lub właściwościach zdarzeń możemy włączyć dokładne logowanie na poziomie Trace. W tym celu należy:

  1. uruchomić formularz w trybie devMode (poprzez dodanie na końcu adresu URL &devMode=true)
  2. upewnić się że konsola wyświetla wiadomości na poziomie Trace/Verbose ( w Google Chrome poziom Verbose jest domyślnie wyłączony)

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: {…}}

Znane błędy

EventAction: No active context found: most likely this code was invoked asynchronously, did you forgot 'this.async()'?

Błąd oznacza próbę interakcji z akcją, jej context'em lub parametrami po wykonaniu akcji bądź w przypadku akcji asynchronicznych, bez wcześniejszego użycia funkcji this.async(). Patrz Akcje asynchroniczne

 

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:

  1. task form - so called system events (e.g. changing the value of a variable, clicking on a button)
  2. 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):

EventAction
change
  1. Action1
  2. Action3
success (Action1)
  1. Action2

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:

sequenceDiagram
autonumber
participant Form
participant EventsController
participant Action1
participant Action2
participant Action3

Form->>EventsController: event:change
activate EventsController
EventsController->>Form: restore()
deactivate EventsController
activate Form
Form->>EventsController: return context:1
deactivate Form
 
activate EventsController
EventsController->>Action1: on(change): action1(context:1)
deactivate EventsController

activate Action1
Action1-->>EventsController: fireEvent("success")
Action1->>EventsController: return context:2
deactivate Action1

activate EventsController
EventsController->>Action3: on(action1_success): action3(context:2)
deactivate EventsController
activate Action3
Action3->>EventsController: return context:3
deactivate Action3

activate EventsController
EventsController->>Action2: on(change): action2(context:3)
deactivate EventsController
activate Action2
Action2->>EventsController: return context:4
deactivate Action2

activate EventsController
EventsController->>Form: apply(context:4)
deactivate EventsController

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.

sequenceDiagram
autonumber
participant Form
participant EventsController
participant Async1
participant Sync1

Form->>EventsController: event:change
activate EventsController
EventsController->>Form: restore()
deactivate EventsController
activate Form
Form->>EventsController: return context:1
deactivate Form

activate EventsController
EventsController->>Async1: on(change): async1(context:1)
deactivate EventsController

activate Async1
Async1-->>EventsController: fireEvent("before_async")
Async1-->>EventsController: start async(context:2)
deactivate Async1

activate EventsController
EventsController->>Sync1: on(async1_before_async): sync1(context:2)
deactivate EventsController

activate Sync1
Sync1->>EventsController: return context:3
deactivate Sync1

activate EventsController
EventsController->>Form: apply(context:3)
deactivate EventsController

Async1 --> Async1: async done
Async1 ->> EventsController: notify()
activate EventsController
EventsController->>Form: restore()
deactivate EventsController
activate Form
Form->>EventsController: return context:4
deactivate Form
activate EventsController
EventsController->>Async1: resume(context:4)
deactivate EventsController

activate Async1
Async1-->>EventsController: fireEvent("after_async")
Async1->>EventsController: return context:5
deactivate Async1
activate EventsController
EventsController->>Sync1: on(async1_after_async): sync1(context:5)
deactivate EventsController

activate Sync1
Sync1->>EventsController: return context:6
deactivate Sync1

activate EventsController
EventsController->>Form: apply(context:6)
deactivate EventsController

 

System events

An overview of task form events for which event actions can be attached.

Global

EventDescriptionParameters
afterLoadFormEvent triggered when all form components are loaded
  • previousUrl: String
    The URL of the page from which the task form was navigated
afterAddDocument

Event raised after document has been attached to the process

  • documentId: String
    Document identifier
  • fileId: Integer
    File identifier
  • versionNo: Integer
    Version number of the attached file
  • docClassId: Integer
    Destination document class identifier
  • docClassName: String
    Destination document class name

Process variables

A form variable that is not part of a dynamic table

EventDescriptionParameters
changeEvent triggered when the value of a variable changes
  • oldValue: String
    The value of the field before the change

    Text value regardless of the type of the variable - this is due to the limitations associated with the type system and the lack of generic types

focusEvent triggered when a field receives focus 
blurEvent triggered when field loses focus 
showEvent triggered when a field is showed 
hideEvent triggered when a field is hidden 

Tabels

EventDescriptionParameters
selectEvent triggered when a row is selected in a table
  • tableId: String
    Dynamic table identifier

  • rowNo: Integer
    Number of the selected table row
cellClickEvent triggered when clicking on a table cell
  • tableId: String
    Dynamic table identifier

  • rowNo: Integer
    Number of the selected table row
  • fieldId: String
    Variable identifier of the clicked cell
afterAddRecordsEvent triggered when records are added to a table
  • tableId: String
    Dynamic table identifier

  • newRecords: TableStore
    Added records in TableStore format
afterRemoveRecordsEvent triggered when records are removed from table
  • tableId: String
    Dynamic table identifier

  • deletedRecords: TableStore
    Removed records in TableStore format
afterAddRecordsEvent triggered when records in table are changed
  • tableId: String
    Dynamic table identifier

  • changedRecords: TableStore
    Changed records in TableStore format
afterRenderEvent triggered after table is rendered 
showEvent triggered after table discovery 
hideEvent triggered when table is hidden 

Buttons

Buttons available on the form

EventDescriptionParameters
clickEvent triggered when the button is clicked

 

 

 

disableEvent triggered when button is locked 
enableEvent triggered when button is unlocked 
showEvent triggered when button is showed 
hideEvent 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: Akcje reagujące na zdarzenia

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
FunctionDescription

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
FunctionDescription

set(newValue): this

Changing the value of a variable
get(): ObjectReturns the current value of the variable
VariableSetContext
FunctionDescription

variable(id).add(newValue): this

 

Adding a new value (row)
variable(id).set(newValues): thisChanging the value of a variable in a table
variable(id).get(): Object[]Returns the current value of a variable (array)
addRecords(tableStore): thisAdds 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:

  1. run the form in devMode (by adding &devMode=true to the end of the URL)
  2. 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});