Feature Control & Side Effects in RAP: Dynamische UI-Steuerung

kategorie
RAP
Veröffentlicht
autor
Johannes

Feature Control ermöglicht die dynamische Steuerung von UI-Elementen wie Feldern, Aktionen und Operationen basierend auf dem aktuellen Datenzustand. Side Effects sorgen für automatische UI-Updates, wenn sich Feldwerte ändern.

Wofür Feature Control?

Feature Control beantwortet Fragen wie:

  • Darf der Benutzer diese Instanz löschen?
  • Soll dieses Feld schreibgeschützt sein?
  • Ist diese Aktion für den aktuellen Status verfügbar?
  • Darf der Benutzer die Assoziationen ändern?

Instance vs. Global Feature Control

AspektInstance Feature ControlGlobal Feature Control
GeltungsbereichPro InstanzFür alle Instanzen
AuswertungBei jeder Instanz einzelnEinmal für die Entität
AnwendungsfallStatusabhängige FeaturesBerechtigungsabhängige Features
PerformanceHöher (n Aufrufe)Niedriger (1 Aufruf)
Beispiel”Stornieren nur bei Status Open""Create nur mit Berechtigung”

Behavior Definition mit Feature Control

Die Features werden in der Behavior Definition deklariert:

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 ( features: instance );
// Felder mit Feature Control
field ( readonly ) TravelID;
field ( features: instance ) Status, Description;
// Aktionen mit Feature Control
action ( features: instance ) cancel;
action ( features: instance ) accept;
// Globales Feature Control für Create
static features;
}

Feature-Optionen

DeklarationBedeutung
features: instanceDynamisch pro Instanz
features: globalDynamisch global (bei static actions)
readonlyImmer schreibgeschützt
mandatoryPflichtfeld
readonly:updateNur beim Update schreibgeschützt

Instance Feature Control Implementation

Die Methode get_instance_features bestimmt die Features pro Instanz:

CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_features FOR INSTANCE FEATURES
IMPORTING keys REQUEST requested_features FOR Travel
RESULT result.
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD get_instance_features.
" Aktuelle Daten lesen
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( Status BeginDate )
WITH CORRESPONDING #( keys )
RESULT DATA(travels).
" Features pro Instanz setzen
result = VALUE #( FOR travel IN travels
LET is_open = COND #( WHEN travel-Status = 'O' THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
is_accepted = COND #( WHEN travel-Status = 'A' THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
is_readonly = COND #( WHEN travel-Status <> 'O' THEN if_abap_behv=>fc-f-read_only
ELSE if_abap_behv=>fc-f-unrestricted )
IN (
%tky = travel-%tky
" Operations
%delete = is_open
" Fields
%field-Status = is_readonly
%field-Description = is_readonly
" Actions
%action-cancel = is_open
%action-accept = is_open
)
).
ENDMETHOD.
ENDCLASS.

Feature-Konstanten

KonstanteTypBedeutung
if_abap_behv=>fc-o-enabledOperationAktiviert
if_abap_behv=>fc-o-disabledOperationDeaktiviert
if_abap_behv=>fc-f-read_onlyFieldSchreibgeschützt
if_abap_behv=>fc-f-mandatoryFieldPflichtfeld
if_abap_behv=>fc-f-unrestrictedFieldKeine Einschränkung
if_abap_behv=>fc-f-mandatory_and_read_onlyFieldPflicht und readonly

Global Feature Control

Global Feature Control steuert Features unabhängig von einzelnen Instanzen:

CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_global_features FOR GLOBAL FEATURES
IMPORTING REQUEST requested_features FOR Travel
RESULT result.
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD get_global_features.
" Berechtigungsprüfung für Create
DATA(has_create_auth) = check_create_authorization( ).
" Prüfen ob heute ein Arbeitstag ist
DATA(is_workday) = check_workday( ).
result = VALUE #(
" Create nur mit Berechtigung
%create = COND #( WHEN has_create_auth = abap_true
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
" Static Action nur an Arbeitstagen
%action-massUpdate = COND #( WHEN is_workday = abap_true
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
).
ENDMETHOD.
ENDCLASS.

Static Actions mit Feature Control

define behavior for ZI_Travel alias Travel
{
// Static Action mit Global Feature Control
static action ( features: global ) massUpdate result [1] $self;
static action ( features: global ) generateReport;
// Reguläre Features
static features;
}

Side Effects: Automatische UI-Updates

Side Effects definieren, welche Teile der UI aktualisiert werden sollen, wenn sich Feldwerte ändern.

Side Effects in der Behavior Definition

define behavior for ZI_Travel alias Travel
{
// Side Effect: Wenn BeginDate oder EndDate sich ändert, berechne Duration
side effects
{
field BeginDate affects field Duration;
field EndDate affects field Duration;
// Mehrere Felder beeinflussen ein Zielfeld
field ( BeginDate, EndDate ) affects field Duration, field TotalPrice;
// Feld beeinflusst gesamte Entität
field Currency affects entity;
// Determination triggert Side Effect
determine action calculatePrice executed on field BeginDate affects field TotalPrice;
}
}

Side Effects für Assoziationen

define behavior for ZI_Travel alias Travel
{
side effects
{
// Änderungen an Child-Entitäten beeinflussen Parent
field _Bookings affects field TotalPrice;
// Feld in Parent beeinflusst Children
field Currency affects entity _Bookings;
}
}

Side Effects für Actions

define behavior for ZI_Travel alias Travel
{
action recalculate;
side effects
{
// Nach Aktion werden diese Felder aktualisiert
action recalculate affects field TotalPrice, field Status;
// Aktion beeinflusst gesamte Entität und Assoziationen
action copyTravel affects entity, entity _Bookings;
}
}

Kombinierte Beispiele

Status-abhängige Felder und Aktionen

METHOD get_instance_features.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( Status TravelID )
WITH CORRESPONDING #( keys )
RESULT DATA(travels).
result = VALUE #( FOR travel IN travels (
%tky = travel-%tky
" Status Open: Alles editierbar
%field-BeginDate = COND #( WHEN travel-Status = 'O'
THEN if_abap_behv=>fc-f-unrestricted
ELSE if_abap_behv=>fc-f-read_only )
%field-EndDate = COND #( WHEN travel-Status = 'O'
THEN if_abap_behv=>fc-f-unrestricted
ELSE if_abap_behv=>fc-f-read_only )
%field-CustomerID = COND #( WHEN travel-Status = 'O'
THEN if_abap_behv=>fc-f-mandatory
ELSE if_abap_behv=>fc-f-read_only )
" Delete nur bei Status Open
%delete = COND #( WHEN travel-Status = 'O'
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
" Accept nur bei Status Open
%action-accept = COND #( WHEN travel-Status = 'O'
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
" Reject nur bei Status Accepted
%action-reject = COND #( WHEN travel-Status = 'A'
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
) ).
ENDMETHOD.

Side Effects mit Determination

define behavior for ZI_Travel alias Travel
{
// Determination für Preisberechnung
determination calculatePrice on modify { field BeginDate; field EndDate; }
side effects
{
// Wenn Determination ausgeführt wird, aktualisiere TotalPrice in UI
determine action calculatePrice executed on field BeginDate
affects field TotalPrice, field Currency;
determine action calculatePrice executed on field EndDate
affects field TotalPrice;
}
}

Field-Abhängigkeiten in der Projection

In der Projection können zusätzliche UI-Annotationen gesetzt werden:

define root view entity ZC_Travel
provider contract transactional_query
as projection on ZI_Travel
{
@UI.hidden: true
key TravelUUID,
@UI.identification: [{ position: 10 }]
@UI.lineItem: [{ position: 10 }]
TravelID,
@UI.identification: [{ position: 20, criticality: 'StatusCriticality' }]
Status,
// Dynamische Sichtbarkeit über Hidden-Binding
@UI.hidden: 'HideInternalNote'
InternalNote,
// Calculated Field für Criticality
case Status
when 'O' then 3 " Green
when 'A' then 2 " Yellow
when 'X' then 1 " Red
end as StatusCriticality
}

Best Practices

  1. Performance beachten: Instance Feature Control wird für jede Instanz aufgerufen. Minimiere Datenbankzugriffe.

  2. READ ENTITIES verwenden: Nutze IN LOCAL MODE um Berechtigungsprüfungen zu vermeiden.

  3. Konsistente Logik: Stelle sicher, dass Feature Control und Validierungen konsistent sind.

  4. Side Effects gezielt einsetzen: Zu viele Side Effects können die Performance beeinträchtigen.

  5. Requested Features prüfen: Nur angeforderte Features berechnen:

METHOD get_instance_features.
" Nur berechnen wenn angefordert
CHECK requested_features-%delete = if_abap_behv=>mk-on OR
requested_features-%action-cancel = if_abap_behv=>mk-on.
" ... Feature-Berechnung
ENDMETHOD.
  1. Globale Features für Berechtigungen: Nutze Global Feature Control für berechtigungsbasierte Einschränkungen.

Weiterführende Themen