Manchmal reicht ein einfacher Button-Klick nicht aus. Bevor eine Buchung storniert wird, soll der Benutzer einen Grund angeben. Vor einer Umbuchung müssen neue Daten erfasst werden. Popup-Aktionen in RAP ermöglichen genau das: strukturierte Benutzereingaben vor der Ausführung einer Action.
Das Konzept: Action mit Parameter-Entity
Eine Popup-Aktion besteht aus drei Komponenten:
- Abstract Entity: Definiert die Struktur des Popups (Felder, Datentypen)
- Behavior Definition: Verknüpft die Action mit dem Parameter
- Implementation: Verarbeitet die eingegebenen Werte
┌─────────────────────────────────────────────────────────────────┐│ Buchung stornieren [X] │├─────────────────────────────────────────────────────────────────┤│ ││ Stornierungsgrund * ││ ┌─────────────────────────────────────┐ ││ │ Kundenänderung ▼ │ ││ └─────────────────────────────────────┘ ││ ││ Bemerkung ││ ┌─────────────────────────────────────┐ ││ │ Kunde hat umgebucht auf späteren │ ││ │ Flug nach München. │ ││ └─────────────────────────────────────┘ ││ ││ ☑ Kunde per E-Mail benachrichtigen ││ ││ Erstattungsbetrag ││ ┌───────────────┐ EUR ││ │ 450,00 │ ││ └───────────────┘ ││ ││ [ Abbrechen ] [ Stornieren ] │└─────────────────────────────────────────────────────────────────┘Schritt 1: Abstract Entity definieren
Die Abstract Entity beschreibt die Felder des Popup-Dialogs:
@EndUserText.label: 'Stornierungsdaten'define abstract entity ZA_CancelBookingParams{ @EndUserText.label: 'Stornierungsgrund' @Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_CancellationReasonVH', element: 'ReasonCode' } }] CancellationReason : abap.char(2);
@EndUserText.label: 'Bemerkung' @UI.multiLineText: true Remarks : abap.string(500);
@EndUserText.label: 'Kunde benachrichtigen' NotifyCustomer : abap_boolean;
@EndUserText.label: 'Erstattungsbetrag' @Semantics.amount.currencyCode: 'Currency' RefundAmount : abap.curr(15,2);
@EndUserText.label: 'Währung' @Semantics.currencyCode: true Currency : abap.cuky;}Wichtige Annotationen für das Popup:
| Annotation | Wirkung |
|---|---|
@EndUserText.label | Feldbezeichnung im Popup |
@Consumption.valueHelpDefinition | Dropdown/Value Help |
@UI.multiLineText: true | Mehrzeiliges Textfeld |
@Semantics.amount.currencyCode | Währungsfeld mit Formatierung |
Schritt 2: Behavior Definition
Verknüpfen Sie die Action mit dem Parameter:
managed implementation in class zbp_i_flightbooking unique;strict ( 2 );
define behavior for ZI_FlightBooking alias FlightBookingpersistent table zflight_booklock masterauthorization master ( instance ){ // Standard CRUD create; update; delete;
// Action MIT Parameter-Entity action cancelBooking parameter ZA_CancelBookingParams result [1] $self;
// Action ohne Parameter (zum Vergleich) action confirmBooking result [1] $self;
// Validierung für Stornierung validation validateCancellation on save { field BookingStatus; }}Das Schlüsselwort parameter bewirkt, dass Fiori Elements automatisch einen Dialog generiert.
Schritt 3: Implementation der Action
In der Behavior Implementation Class verarbeiten Sie die Parameter:
CLASS lhc_flightbooking DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS cancelBooking FOR MODIFY IMPORTING keys FOR ACTION FlightBooking~cancelBooking RESULT result.ENDCLASS.
CLASS lhc_flightbooking IMPLEMENTATION. METHOD cancelBooking. " Aktuelle Daten lesen READ ENTITIES OF zi_flightbooking IN LOCAL MODE ENTITY FlightBooking FIELDS ( booking_id booking_status flight_price currency_code ) WITH CORRESPONDING #( keys ) RESULT DATA(bookings).
LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). " Buchung aus Ergebnis holen READ TABLE bookings INTO DATA(booking) WITH KEY booking_id = <key>-booking_id.
IF sy-subrc <> 0. " Buchung nicht gefunden APPEND VALUE #( %tky = <key>-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Buchung nicht gefunden' ) ) TO reported-flightbooking. APPEND VALUE #( %tky = <key>-%tky ) TO failed-flightbooking. CONTINUE. ENDIF.
" === Parameter aus dem Popup auslesen === DATA(cancel_reason) = <key>-%param-CancellationReason. DATA(remarks) = <key>-%param-Remarks. DATA(notify) = <key>-%param-NotifyCustomer. DATA(refund_amount) = <key>-%param-RefundAmount. DATA(currency) = <key>-%param-Currency.
" Validierung: Pflichtfeld Stornierungsgrund IF cancel_reason IS INITIAL. APPEND VALUE #( %tky = <key>-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Stornierungsgrund ist ein Pflichtfeld' ) ) TO reported-flightbooking. APPEND VALUE #( %tky = <key>-%tky ) TO failed-flightbooking. CONTINUE. ENDIF.
" Validierung: Erstattung nicht höher als Buchungspreis IF refund_amount > booking-flight_price. APPEND VALUE #( %tky = <key>-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = |Erstattung kann nicht höher als { booking-flight_price } { booking-currency_code } sein| ) ) TO reported-flightbooking. APPEND VALUE #( %tky = <key>-%tky ) TO failed-flightbooking. CONTINUE. ENDIF.
" Status auf Storniert setzen MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE ENTITY FlightBooking UPDATE FIELDS ( booking_status cancellation_reason remarks ) WITH VALUE #( ( %tky = <key>-%tky booking_status = 'X' " Cancelled cancellation_reason = cancel_reason remarks = remarks ) ).
" Optional: Kunde benachrichtigen IF notify = abap_true. send_cancellation_email( booking_id = booking-booking_id reason = cancel_reason remarks = remarks ). ENDIF.
" Optional: Erstattung verarbeiten IF refund_amount > 0. process_refund( booking_id = booking-booking_id amount = refund_amount currency = currency ). ENDIF. ENDLOOP.
" Aktualisierte Daten zurückgeben READ ENTITIES OF zi_flightbooking IN LOCAL MODE ENTITY FlightBooking ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(updated_bookings).
result = VALUE #( FOR bk IN updated_bookings ( %tky = bk-%tky %param = bk ) ).
" Success-Nachricht APPEND VALUE #( %msg = new_message_with_text( severity = if_abap_behv_message=>severity-success text = |{ lines( updated_bookings ) } Buchung(en) storniert| ) ) TO reported-flightbooking. ENDMETHOD.ENDCLASS.UI-Annotationen für das Popup-Layout
Verfeinern Sie das Popup-Layout mit zusätzlichen Annotationen:
@EndUserText.label: 'Stornierungsdaten'define abstract entity ZA_CancelBookingParams{ @EndUserText.label: 'Stornierungsgrund' @UI.defaultValue: 'KA' @Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_CancellationReasonVH', element: 'ReasonCode' }, useForValidation: true }] CancellationReason : abap.char(2);
@EndUserText.label: 'Bemerkung' @UI.multiLineText: true @UI.placeholder: 'Optional: Zusätzliche Informationen zur Stornierung' Remarks : abap.string(500);
@EndUserText.label: 'Kunde benachrichtigen' @UI.defaultValue: 'true' NotifyCustomer : abap_boolean;
@EndUserText.label: 'Erstattungsbetrag' @Semantics.amount.currencyCode: 'Currency' @UI.defaultValue: '0.00' RefundAmount : abap.curr(15,2);
@EndUserText.label: 'Währung' @Semantics.currencyCode: true @UI.defaultValue: 'EUR' @Consumption.valueHelpDefinition: [{ entity: { name: 'I_Currency', element: 'Currency' } }] Currency : abap.cuky;}Wichtige UI-Annotationen
| Annotation | Wirkung | Beispiel |
|---|---|---|
@UI.defaultValue | Vorbelegter Wert | 'EUR' |
@UI.placeholder | Platzhaltertext | 'Bemerkung eingeben...' |
@UI.multiLineText | Mehrzeiliges Eingabefeld | Für lange Texte |
@UI.hidden | Feld verstecken | Technische Felder |
@Consumption.valueHelpDefinition | Value Help/Dropdown | Auswahlfelder |
Verschiedene Feldtypen im Popup
Dropdown mit Value Help
@EndUserText.label: 'Priorität'@Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_PriorityVH', element: 'Priority' }}]Priority : abap.char(1);Datumsfeld
@EndUserText.label: 'Neues Flugdatum'@UI.defaultValue: '#(TODAY)'NewFlightDate : abap.dats;Checkbox
@EndUserText.label: 'Bestätigung per E-Mail senden'@UI.defaultValue: 'false'SendConfirmation : abap_boolean;Mehrzeiliger Text
@EndUserText.label: 'Begründung'@UI.multiLineText: trueJustification : abap.string(1000);Währungsbetrag
@EndUserText.label: 'Betrag'@Semantics.amount.currencyCode: 'Currency'Amount : abap.curr(15,2);
@EndUserText.label: 'Währung'@Semantics.currencyCode: trueCurrency : abap.cuky;Mengenfeld
@EndUserText.label: 'Anzahl'@Semantics.quantity.unitOfMeasure: 'Unit'Quantity : abap.quan(13,3);
@EndUserText.label: 'Einheit'@Semantics.unitOfMeasure: trueUnit : abap.unit(3);Vollständiges Flugbuchungs-Beispiel
Hier ein komplettes End-to-End Beispiel für eine Umbuchungs-Aktion:
Abstract Entity: Umbuchungsparameter
@EndUserText.label: 'Umbuchungsparameter'define abstract entity ZA_RescheduleParams{ @EndUserText.label: 'Neues Flugdatum' NewFlightDate : abap.dats;
@EndUserText.label: 'Neue Flugnummer' @Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_FlightConnectionVH', element: 'FlightId' } }] NewFlightId : abap.numc(4);
@EndUserText.label: 'Preisdifferenz zahlen' AcceptPriceDiff : abap_boolean;
@EndUserText.label: 'Bemerkung' @UI.multiLineText: true Remarks : abap.string(500);}Behavior Definition
define behavior for ZI_FlightBooking alias FlightBooking{ action rescheduleBooking parameter ZA_RescheduleParams result [1] $self;}Projection Behavior
projection implementation in class zbp_c_flightbooking unique;
define behavior for ZC_FlightBooking alias FlightBooking{ use action rescheduleBooking;}Implementation
METHOD rescheduleBooking. " Aktuelle Buchungen lesen READ ENTITIES OF zi_flightbooking IN LOCAL MODE ENTITY FlightBooking ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(bookings).
LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). READ TABLE bookings INTO DATA(booking) WITH KEY booking_id = <key>-booking_id.
" === Parameter aus dem Popup === DATA(new_date) = <key>-%param-NewFlightDate. DATA(new_flight) = <key>-%param-NewFlightId. DATA(accept_diff) = <key>-%param-AcceptPriceDiff. DATA(remarks) = <key>-%param-Remarks.
" Validierung: Datum in der Zukunft IF new_date < cl_abap_context_info=>get_system_date( ). APPEND VALUE #( %tky = <key>-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Flugdatum muss in der Zukunft liegen' ) ) TO reported-flightbooking. APPEND VALUE #( %tky = <key>-%tky ) TO failed-flightbooking. CONTINUE. ENDIF.
" Preisdifferenz berechnen DATA(new_price) = get_flight_price( new_flight ). DATA(price_diff) = new_price - booking-flight_price.
" Preiserhöhung ohne Akzeptanz = Fehler IF price_diff > 0 AND accept_diff = abap_false. APPEND VALUE #( %tky = <key>-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = |Preisdifferenz von { price_diff } { booking-currency_code } muss akzeptiert werden| ) ) TO reported-flightbooking. APPEND VALUE #( %tky = <key>-%tky ) TO failed-flightbooking. CONTINUE. ENDIF.
" Umbuchung durchführen MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE ENTITY FlightBooking UPDATE FIELDS ( flight_date flight_id flight_price remarks ) WITH VALUE #( ( %tky = <key>-%tky flight_date = new_date flight_id = new_flight flight_price = new_price remarks = remarks ) ). ENDLOOP.
" Ergebnis zurückgeben READ ENTITIES OF zi_flightbooking IN LOCAL MODE ENTITY FlightBooking ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(updated).
result = VALUE #( FOR bk IN updated ( %tky = bk-%tky %param = bk ) ).
" Success-Nachricht APPEND VALUE #( %msg = new_message_with_text( severity = if_abap_behv_message=>severity-success text = |{ lines( updated ) } Buchung(en) umgebucht| ) ) TO reported-flightbooking.ENDMETHOD.UI Integration in CDS View
@EndUserText.label: 'Flugbuchung'define view entity ZC_FlightBooking as projection on ZI_FlightBooking{ @UI.lineItem: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'cancelBooking', label: 'Stornieren', criticality: #NEGATIVE }, { type: #FOR_ACTION, dataAction: 'rescheduleBooking', label: 'Umbuchen' } ] @UI.identification: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'rescheduleBooking', label: 'Umbuchen' } ] key BookingId,
// weitere Felder...}Validierung von Popup-Eingaben
Im Action-Code (empfohlen)
" Pflichtfeld prüfenIF <key>-%param-CancellationReason IS INITIAL. APPEND VALUE #( %tky = <key>-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Stornierungsgrund ist erforderlich' ) ) TO reported-flightbooking. APPEND VALUE #( %tky = <key>-%tky ) TO failed-flightbooking. RETURN.ENDIF.Mit Validation im Behavior
define behavior for ZI_FlightBooking{ action cancelBooking parameter ZA_CancelBookingParams result [1] $self;
" Validation wird nach der Action ausgeführt validation validateAfterCancel on save { field booking_status; }}Best Practices für Popup-Aktionen
1. Pflichtfelder kennzeichnen
@EndUserText.label: 'Grund *' " Sternchen im Label@ObjectModel.mandatory: true " PflichtfeldReason : abap.char(2);2. Sinnvolle Standardwerte
@UI.defaultValue: 'EUR' " Für Währung@UI.defaultValue: 'true' " Für Checkbox@UI.defaultValue: '#(TODAY)' " Für Datum3. Value Helps für Auswahlfelder
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_ReasonVH', element: 'Code' }, useForValidation: true}]4. Klare Fehlermeldungen
" ❌ Schlechttext = 'Fehler bei der Eingabe'
" ✅ Guttext = |Erstattung ({ refund_amount } { currency }) kann nicht höher als Buchungspreis ({ booking-flight_price } { booking-currency_code }) sein|5. Popup-Größe begrenzen
Maximal 5-6 Felder pro Popup. Bei komplexeren Eingaben: Wizard oder separate Seite.
Popup vs. Inline-Bearbeitung
| Szenario | Empfehlung |
|---|---|
| 1-2 zusätzliche Werte | Popup-Action |
| Komplexe Formulare | Separate Object Page |
| Massenänderung | Inline-Edit oder eigene App |
| Bestätigung ohne Eingabe | Action ohne Parameter |
Zusammenfassung
Popup-Aktionen in RAP ermöglichen interaktive Benutzereingaben vor der Ausführung:
| Komponente | Aufgabe |
|---|---|
| Abstract Entity | Definiert Popup-Felder und Datentypen |
| Behavior Definition | Verknüpft Action mit parameter |
| Implementation | Liest %param und verarbeitet Werte |
| UI-Annotationen | Steuert Aussehen und Verhalten |
Mit wenigen Annotationen und klarer Struktur erstellen Sie benutzerfreundliche Dialoge, die nahtlos in Fiori Elements integriert sind.
Weiterführende Artikel: RAP Actions und Functions für Action-Grundlagen, RAP Nachrichten für Fehlerbehandlung, RAP CDS Pattern für vereinfachte Architektur und CDS Annotations für weitere Annotationsmöglichkeiten.