Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 20 Next »

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:

%%{init: {'theme':'default'}}%% 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.

%%{init: {'theme':'default'}}%% 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

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
afterAddRecordsZdarzenie 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");
});

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
variableSet(tableId) : VariableSetContext

Zwraca VariableSetContext tabeli o podanym identyfikatorze

VariableContext
FunkcjaOpis

set(newValue): this

Zmiana wartości zmiennej
get(): ObjectZwraca aktualną wartość zmiennej
VariableSetContext
FunkcjaOpis

variable(id).add(newValue): this

 

Dodanie nowej wartości (wiersza)
variable(id).set(newValues): thisZmiana wartości zmiennej w tabeli
variable(id).get(): Object[]Zwraca aktualną wartość zmiennej (tablica)
addRecords(tableStore): thisDodaje rekordy opisane podanym TableStore'em

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)

Przykład logów Trace
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

 

  • No labels