RAP Side Effects: Automatische UI-Aktualisierungen

kategorie
RAP
Veröffentlicht
autor
Johannes

Side Effects in RAP definieren, welche UI-Elemente aktualisiert werden sollen, wenn sich Feldwerte ändern oder Aktionen ausgeführt werden. Sie sorgen für eine reaktive Benutzeroberfläche ohne manuelles Neuladen.

Wofür Side Effects?

Side Effects beantworten Fragen wie:

  • Welche Felder müssen aktualisiert werden, wenn sich der Preis ändert?
  • Welche Bereiche der UI müssen nach einer Aktion neu geladen werden?
  • Wie werden berechnete Felder automatisch aktualisiert?

Typische Anwendungsfälle

SzenarioTriggerBetroffene Felder
PreisberechnungMenge, EinzelpreisGesamtpreis
DatumsberechnungStart- oder EnddatumDauer, Arbeitstage
WährungsumrechnungWährungAlle Betragsfelder
StatusänderungAction ausführenStatus, Verfügbarkeit

Side Effects in der Behavior Definition

Side Effects werden deklarativ in der Behavior Definition festgelegt:

managed implementation in class zbp_i_travel unique;
strict ( 2 );
define behavior for ZI_Travel alias Travel
persistent table ztravel
lock master
authorization master ( instance )
etag master LastChangedAt
{
create;
update;
delete;
// Side Effects Block
side effects
{
// Field-Level Side Effects
field BeginDate affects field Duration;
field EndDate affects field Duration;
// Action-Level Side Effects
action calculatePrice affects field TotalPrice, field Currency;
// Determination Side Effects
determine action Prepare affects field Status;
}
}

Field-Level Side Effects

Field-Level Side Effects definieren, welche Felder aktualisiert werden, wenn ein bestimmtes Feld geändert wird.

Einfacher Field Side Effect

side effects
{
// Wenn BeginDate sich ändert, aktualisiere Duration
field BeginDate affects field Duration;
// Wenn EndDate sich ändert, aktualisiere Duration
field EndDate affects field Duration;
}

Mehrere Trigger-Felder

side effects
{
// Mehrere Felder triggern die gleiche Aktualisierung
field ( BeginDate, EndDate ) affects field Duration;
// Preis-Berechnung bei Mengen- oder Preisänderung
field ( Quantity, UnitPrice ) affects field TotalPrice;
}

Mehrere betroffene Felder

side effects
{
// Ein Trigger-Feld beeinflusst mehrere Zielfelder
field Quantity affects field TotalPrice, field TaxAmount, field NetPrice;
// Währungsänderung beeinflusst alle Betragsfelder
field CurrencyCode affects field TotalPrice, field TaxAmount, field Discount;
}

$self - Die gesamte Instanz

Mit $self wird die gesamte Instanz als betroffen markiert:

side effects
{
// Währungsänderung: Alle Felder neu laden
field CurrencyCode affects $self;
// Kundenänderung: Gesamte Instanz aktualisieren
field CustomerId affects $self;
}

Action-Level Side Effects

Action-Level Side Effects definieren, welche Felder nach einer Aktion aktualisiert werden.

define behavior for ZI_Travel alias Travel
{
// Aktionen definieren
action calculatePrice result [1] $self;
action applyDiscount result [1] $self;
action setStatus result [1] $self;
side effects
{
// Nach Preisberechnung diese Felder aktualisieren
action calculatePrice affects field TotalPrice, field TaxAmount;
// Nach Rabatt-Anwendung
action applyDiscount affects field TotalPrice, field DiscountAmount;
// Statusänderung beeinflusst Status-Feld
action setStatus affects field Status;
}
}

Action mit komplexen Side Effects

side effects
{
// Action beeinflusst mehrere Bereiche
action recalculateAll affects
field TotalPrice,
field TaxAmount,
field NetPrice,
field Currency,
entity _Bookings; // Auch Assoziationen
// Action beeinflusst gesamte Entität
action copyFromTemplate affects $self;
}

Determination Side Effects

Wenn Determinations ausgeführt werden, können Side Effects die UI über berechnete Werte informieren.

Behavior Definition

define behavior for ZI_Travel alias Travel
{
// Determination für Preisberechnung
determination calculateTotalPrice on modify { field Quantity; field UnitPrice; }
// Determination für Status
determination setInitialStatus on modify { create; }
side effects
{
// Wenn calculateTotalPrice auf Quantity-Änderung ausgeführt wird
determine action calculateTotalPrice executed on field Quantity
affects field TotalPrice;
// Wenn calculateTotalPrice auf UnitPrice-Änderung ausgeführt wird
determine action calculateTotalPrice executed on field UnitPrice
affects field TotalPrice;
}
}

Vollständiges Beispiel mit Determination

managed implementation in class zbp_i_travel unique;
strict ( 2 );
define behavior for ZI_Travel alias Travel
persistent table ztravel
{
create;
update;
delete;
// Determinations
determination calculateDuration on modify { field BeginDate; field EndDate; }
determination calculatePrice on modify { field Quantity; field UnitPrice; field CurrencyCode; }
side effects
{
// Duration-Berechnung
determine action calculateDuration executed on field BeginDate
affects field Duration;
determine action calculateDuration executed on field EndDate
affects field Duration;
// Preis-Berechnung
determine action calculatePrice executed on field Quantity
affects field TotalPrice, field TaxAmount;
determine action calculatePrice executed on field UnitPrice
affects field TotalPrice, field TaxAmount;
determine action calculatePrice executed on field CurrencyCode
affects field TotalPrice, field TaxAmount, field Currency;
}
}

Implementation der Determination

CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS calculateDuration FOR DETERMINE ON MODIFY
IMPORTING keys FOR Travel~calculateDuration.
METHODS calculatePrice FOR DETERMINE ON MODIFY
IMPORTING keys FOR Travel~calculatePrice.
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD calculateDuration.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( BeginDate EndDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Duration )
WITH VALUE #( FOR ls_travel IN lt_travel
WHERE ( BeginDate IS NOT INITIAL AND EndDate IS NOT INITIAL )
( %tky = ls_travel-%tky
Duration = ls_travel-EndDate - ls_travel-BeginDate ) )
REPORTED reported.
ENDMETHOD.
METHOD calculatePrice.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( Quantity UnitPrice CurrencyCode )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( TotalPrice TaxAmount Currency )
WITH VALUE #( FOR ls_travel IN lt_travel
LET lv_total = ls_travel-Quantity * ls_travel-UnitPrice
lv_tax = lv_total * '0.19'
IN
( %tky = ls_travel-%tky
TotalPrice = lv_total
TaxAmount = lv_tax
Currency = ls_travel-CurrencyCode ) )
REPORTED reported.
ENDMETHOD.
ENDCLASS.

Side Effects mit Assoziationen

Side Effects können auch Assoziationen betreffen:

define behavior for ZI_Travel alias Travel
{
association _Bookings { create; }
side effects
{
// Änderungen an Bookings beeinflussen Travel
field _Bookings affects field TotalPrice, field BookingCount;
// Währungsänderung in Travel beeinflusst alle Bookings
field CurrencyCode affects entity _Bookings;
}
}
define behavior for ZI_Booking alias Booking
{
association _Travel;
side effects
{
// Preis in Booking beeinflusst Gesamtpreis in Travel
field Price affects field _Travel.TotalPrice;
}
}

Hierarchische Side Effects

define behavior for ZI_Travel alias Travel
{
side effects
{
// Alle Bookings aktualisieren wenn sich Währung ändert
field CurrencyCode affects entity _Bookings, entity _Passengers;
// Nach Copy-Action alle Child-Entitäten neu laden
action copyWithDetails affects $self, entity _Bookings, entity _Passengers;
}
}

Target-Felder und $self

Einzelne Zielfelder

side effects
{
field Quantity affects field TotalPrice;
}

Mehrere Zielfelder

side effects
{
field Quantity affects field TotalPrice, field TaxAmount, field NetPrice;
}

Gesamte Entität ($self)

side effects
{
// Bei Statusänderung: Komplette Instanz neu laden
field Status affects $self;
// Bei Kopie: Alles aktualisieren
action copy affects $self;
}

Entity (Assoziationen)

side effects
{
// Child-Entitäten aktualisieren
field CurrencyCode affects entity _Bookings;
// Parent-Entität aktualisieren
field BookingPrice affects entity _Travel;
}

Vollständiges Beispiel

managed implementation in class zbp_i_travel unique;
strict ( 2 );
define behavior for ZI_Travel alias Travel
persistent table ztravel
draft table zdraft_travel
lock master total etag LastChangedAt
authorization master ( instance )
etag master LastChangedAt
{
create;
update;
delete;
// Felder
field ( readonly ) TravelId, CreatedBy, CreatedAt;
field ( mandatory ) CustomerId;
// Assoziationen
association _Bookings { create; }
// Determinations
determination calculateDuration on modify { field BeginDate; field EndDate; }
determination calculateTotalPrice on modify { field _Bookings; }
determination setInitialStatus on modify { create; }
// Actions
action ( features: instance ) acceptTravel result [1] $self;
action recalculatePrice result [1] $self;
// Side Effects
side effects
{
// Field-Level
field BeginDate affects field Duration, field DaysUntilStart;
field EndDate affects field Duration;
field CustomerId affects field CustomerName, field CustomerRating;
// Währung beeinflusst alle Betragsfelder
field CurrencyCode affects
field TotalPrice,
field TaxAmount,
entity _Bookings;
// Determination Side Effects
determine action calculateDuration executed on field BeginDate
affects field Duration;
determine action calculateDuration executed on field EndDate
affects field Duration;
determine action calculateTotalPrice executed on field _Bookings
affects field TotalPrice, field BookingCount;
// Action Side Effects
action acceptTravel affects field Status, field ApprovedAt, field ApprovedBy;
action recalculatePrice affects field TotalPrice, field TaxAmount, entity _Bookings;
}
}
define behavior for ZI_Booking alias Booking
persistent table zbooking
draft table zdraft_booking
lock dependent by _Travel
authorization dependent by _Travel
etag master LastChangedAt
{
create;
update;
delete;
association _Travel;
side effects
{
// Preis-Änderung in Booking aktualisiert Travel-Summe
field Price affects field _Travel.TotalPrice;
}
}

Best Practices

  1. Minimale Side Effects: Definiere nur die wirklich benötigten Side Effects – zu viele können die Performance beeinträchtigen

  2. Spezifische Felder statt $self: Nutze $self nur, wenn wirklich die gesamte Entität betroffen ist

  3. Determination + Side Effect: Kombiniere Determinations mit Side Effects für automatische Berechnungen

  4. Bidirektionale Assoziationen: Achte bei Parent-Child-Beziehungen auf korrekte Richtung der Side Effects

  5. Performance: Side Effects lösen UI-Requests aus – teste die Performance bei komplexen Szenarien

  6. Konsistenz: Stelle sicher, dass die Side Effect-Definitionen mit der tatsächlichen Logik übereinstimmen

" RICHTIG: Side Effect passt zur Determination
determination calculateTotal on modify { field Quantity; field Price; }
side effects
{
determine action calculateTotal executed on field Quantity affects field TotalPrice;
determine action calculateTotal executed on field Price affects field TotalPrice;
}
" FALSCH: Side Effect ohne entsprechende Berechnung
side effects
{
field Quantity affects field TotalPrice; " Wer berechnet TotalPrice?
}

Weiterführende Themen