RAP Tutorial Teil 2: Advanced Features - Actions, Validations & Determinations

kategorie
Tutorial
Veröffentlicht
autor
Johannes

Willkommen zu Teil 2 der RAP Tutorial-Serie! In Teil 1 hast du eine funktionierende Fiori App gebaut. Jetzt machen wir sie richtig smart mit Business-Logik.

🎯 Was du lernen wirst

  • Actions: Custom Buttons für Geschäftsoperationen
  • Validations: Datenprüfung (z.B. ISBN-Format)
  • Determinations: Automatische Werte setzen
  • Value Helps: Dropdown-Listen
  • Feature Control: Buttons dynamisch ein/ausblenden

Basis: Wir bauen auf der Book Management App aus Teil 1 auf.

Geschätzte Zeit: 45-60 Minuten


📋 Voraussetzungen

  • Teil 1 abgeschlossen
  • ✅ Book Management App läuft
  • ✅ ADT (Eclipse) geöffnet

Feature 1: Status-Management mit Actions

Wir fügen einen Status hinzu (New, Reading, Finished) und Actions zum Ändern des Status.

Schritt 1: Tabelle erweitern

Öffne ZBOOK_TAB und füge hinzu:

define table zbook_tab {
// ... bestehende Felder ...
status : abap.char(1); // N=New, R=Reading, F=Finished
// ... created_by, etc. ...
}

Aktivieren: Ctrl+F3

Schritt 2: CDS Views erweitern

In ZI_BOOK:

define root view entity ZI_BOOK
as select from zbook_tab
{
// ... bestehende Felder ...
status as Status,
// Status-Text via Association
case status
when 'N' then 'Neu'
when 'R' then 'Lese ich gerade'
when 'F' then 'Gelesen'
else 'Unbekannt'
end as StatusText,
// ... created_by, etc. ...
}

In ZC_BOOK:

define root view entity ZC_BOOK
as projection on ZI_BOOK
{
// ... bestehende Felder ...
@ObjectModel.text.element: ['StatusText']
Status,
StatusText,
// ... created_by, etc. ...
}

Beide aktivieren!

Schritt 3: Behavior Definition erweitern - Actions hinzufügen

Öffne die Interface BDEF (ZI_BOOK → Behavior Definition):

managed implementation in class zbp_i_book unique;
strict ( 2 );
define behavior for ZI_BOOK alias Book
persistent table zbook_tab
lock master
authorization master ( instance )
etag master LastChangedAt
{
create;
update;
delete;
field ( readonly ) BookId;
field ( readonly ) CreatedBy, CreatedAt, LastChangedBy, LastChangedAt;
field ( numbering : managed ) BookId;
// ⭐ NEU: Actions mit Feature Control
action ( features : instance ) startReading result [1] $self;
action ( features : instance ) markAsFinished result [1] $self;
action ( features : instance ) resetStatus result [1] $self;
// ⭐ NEU: Validation beim Speichern
validation validateIsbn on save { field Isbn; }
validation validatePages on save { field Pages; }
// ⭐ NEU: Determination bei Create
determination setDefaultStatus on modify { create; }
mapping for zbook_tab
{
BookId = book_id;
Title = title;
Author = author;
Publisher = publisher;
Isbn = isbn;
Price = price;
CurrencyCode = currency_code;
Pages = pages;
PublicationYear = publication_year;
Language = language;
Status = status; // NEU
CreatedBy = created_by;
CreatedAt = created_at;
LastChangedBy = last_changed_by;
LastChangedAt = last_changed_at;
}
}

Aktivieren: Ctrl+F3

Schritt 4: Projection BDEF erweitern

Öffne ZC_BOOK Behavior Definition:

projection;
strict ( 2 );
define behavior for ZC_BOOK alias Book
{
use create;
use update;
use delete;
// ⭐ NEU: Actions exposen
use action startReading;
use action markAsFinished;
use action resetStatus;
}

Aktivieren!

Schritt 5: Behavior Implementation - Actions implementieren

ADT hat automatisch ZBP_I_BOOK erstellt. Öffne die Klasse und füge hinzu:

Local Types (am Ende der Klasse):

CLASS lhc_book DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS:
" Feature Control (welche Actions verfügbar?)
get_instance_features FOR INSTANCE FEATURES
IMPORTING keys REQUEST requested_features FOR Book
RESULT result,
" Actions
startReading FOR MODIFY
IMPORTING keys FOR ACTION Book~startReading
RESULT result,
markAsFinished FOR MODIFY
IMPORTING keys FOR ACTION Book~markAsFinished
RESULT result,
resetStatus FOR MODIFY
IMPORTING keys FOR ACTION Book~resetStatus
RESULT result,
" Validations
validateIsbn FOR VALIDATE ON SAVE
IMPORTING keys FOR Book~validateIsbn,
validatePages FOR VALIDATE ON SAVE
IMPORTING keys FOR Book~validatePages,
" Determinations
setDefaultStatus FOR DETERMINE ON MODIFY
IMPORTING keys FOR Book~setDefaultStatus.
ENDCLASS.
CLASS lhc_book IMPLEMENTATION.
" ===== FEATURE CONTROL =====
METHOD get_instance_features.
" Lese aktuelle Bücher
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_books)
FAILED failed.
" Feature Control basierend auf Status
result = VALUE #( FOR ls_book IN lt_books
( %tky = ls_book-%tky
" startReading: Nur verfügbar wenn Status = 'N' (Neu)
%features-%action-startReading = COND #(
WHEN ls_book-Status = 'N' THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled
)
" markAsFinished: Nur verfügbar wenn Status = 'R' (Reading)
%features-%action-markAsFinished = COND #(
WHEN ls_book-Status = 'R' THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled
)
" resetStatus: Immer verfügbar außer bei 'N'
%features-%action-resetStatus = COND #(
WHEN ls_book-Status <> 'N' THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled
)
)
).
ENDMETHOD.
" ===== ACTIONS =====
METHOD startReading.
" Status auf 'R' (Reading) setzen
MODIFY ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
UPDATE FIELDS ( Status )
WITH VALUE #( FOR key IN keys
( %tky = key-%tky
Status = 'R' ) )
FAILED failed
REPORTED reported.
" Aktualisierte Bücher zurückgeben
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT result.
ENDMETHOD.
METHOD markAsFinished.
" Status auf 'F' (Finished) setzen
MODIFY ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
UPDATE FIELDS ( Status )
WITH VALUE #( FOR key IN keys
( %tky = key-%tky
Status = 'F' ) )
FAILED failed
REPORTED reported.
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT result.
ENDMETHOD.
METHOD resetStatus.
" Status auf 'N' (New) zurücksetzen
MODIFY ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
UPDATE FIELDS ( Status )
WITH VALUE #( FOR key IN keys
( %tky = key-%tky
Status = 'N' ) )
FAILED failed
REPORTED reported.
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT result.
ENDMETHOD.
" ===== VALIDATIONS =====
METHOD validateIsbn.
" Bücher lesen
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
FIELDS ( Isbn )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_books).
LOOP AT lt_books INTO DATA(ls_book).
" ISBN muss 10 oder 13 Zeichen lang sein
DATA(lv_isbn_length) = strlen( ls_book-Isbn ).
IF lv_isbn_length <> 10 AND lv_isbn_length <> 13.
APPEND VALUE #(
%tky = ls_book-%tky
%element-Isbn = if_abap_behv=>mk-on
) TO failed-book.
APPEND VALUE #(
%tky = ls_book-%tky
%element-Isbn = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'ISBN muss 10 oder 13 Zeichen lang sein'
)
) TO reported-book.
ENDIF.
" ISBN darf nur Ziffern enthalten (vereinfachte Prüfung)
IF ls_book-Isbn IS NOT INITIAL AND
ls_book-Isbn CO '0123456789' = abap_false.
APPEND VALUE #(
%tky = ls_book-%tky
%element-Isbn = if_abap_behv=>mk-on
) TO failed-book.
APPEND VALUE #(
%tky = ls_book-%tky
%element-Isbn = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'ISBN darf nur Ziffern enthalten'
)
) TO reported-book.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD validatePages.
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
FIELDS ( Pages )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_books).
LOOP AT lt_books INTO DATA(ls_book).
" Seiten müssen > 0 sein
IF ls_book-Pages IS NOT INITIAL AND ls_book-Pages <= 0.
APPEND VALUE #(
%tky = ls_book-%tky
%element-Pages = if_abap_behv=>mk-on
) TO failed-book.
APPEND VALUE #(
%tky = ls_book-%tky
%element-Pages = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Seitenzahl muss größer als 0 sein'
)
) TO reported-book.
ENDIF.
ENDLOOP.
ENDMETHOD.
" ===== DETERMINATIONS =====
METHOD setDefaultStatus.
" Lese neu angelegte Bücher
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_books).
" Setze Default-Status auf 'N' (New)
MODIFY ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
UPDATE FIELDS ( Status )
WITH VALUE #( FOR ls_book IN lt_books
WHERE ( Status IS INITIAL )
( %tky = ls_book-%tky
Status = 'N' ) )
REPORTED reported.
ENDMETHOD.
ENDCLASS.

Aktivieren: Ctrl+F3

Schritt 6: UI-Annotations für Actions

Öffne die Metadata Extension (ZC_BOOK):

@Metadata.layer: #CORE
annotate view ZC_BOOK with
{
@UI.headerInfo: {
typeName: 'Buch',
typeNamePlural: 'Bücher',
title: { value: 'Title' },
description: { value: 'Author' }
}
@UI.facet: [
{
id: 'BookDetails',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Buch-Details',
position: 10
}
]
// ⭐ NEU: Actions in der Liste
@UI.lineItem: [
{ position: 10, importance: #HIGH },
{ type: #FOR_ACTION, dataAction: 'startReading', label: 'Lesen starten' },
{ type: #FOR_ACTION, dataAction: 'markAsFinished', label: 'Als gelesen markieren' },
{ type: #FOR_ACTION, dataAction: 'resetStatus', label: 'Status zurücksetzen' }
]
@UI.identification: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
BookId;
@UI.lineItem: [{ position: 20, importance: #HIGH }]
@UI.identification: [{ position: 20 }]
@UI.selectionField: [{ position: 20 }]
Title;
@UI.lineItem: [{ position: 30, importance: #HIGH }]
@UI.identification: [{ position: 30 }]
@UI.selectionField: [{ position: 30 }]
Author;
@UI.lineItem: [{ position: 40 }]
@UI.identification: [{ position: 40 }]
Publisher;
@UI.identification: [{ position: 50 }]
Isbn;
@UI.lineItem: [{ position: 50 }]
@UI.identification: [{ position: 60 }]
Price;
@UI.identification: [{ position: 70 }]
Pages;
@UI.lineItem: [{ position: 60 }]
@UI.identification: [{ position: 80 }]
PublicationYear;
@UI.identification: [{ position: 90 }]
Language;
// ⭐ NEU: Status anzeigen
@UI.lineItem: [{ position: 35, importance: #HIGH, criticality: 'Status' }]
@UI.identification: [{ position: 35 }]
@UI.selectionField: [{ position: 40 }]
Status;
}

Aktivieren!

Schritt 7: Service Binding neu publishen

  1. Öffne ZUI_BOOK_O4 (Service Binding)
  2. Unpublish klicken
  3. Publish klicken

Schritt 8: Testen! 🎉

  1. Preview öffnen
  2. Buch erstellen
  3. Buttons erscheinen:
    • “Lesen starten” (nur bei Status = Neu)
    • “Als gelesen markieren” (nur bei Status = Lese ich gerade)
    • “Status zurücksetzen” (nicht bei Neu)

Probiere es aus:

  • Klicke “Lesen starten” → Status ändert sich zu “Lese ich gerade”
  • Jetzt erscheint “Als gelesen markieren”
  • Validierung testen: Gib ISBN mit Buchstaben ein → Fehler!

Feature 2: Value Help (Dropdown für Status)

Aktuell muss man den Status manuell eingeben. Besser: Dropdown mit Auswahl!

Schritt 1: Domain Fixed Values erstellen

In ZC_BOOK Metadata Extension:

annotate view ZC_BOOK with
{
// ... andere Felder ...
@UI.lineItem: [{ position: 35, importance: #HIGH }]
@UI.identification: [{ position: 35 }]
@UI.selectionField: [{ position: 40 }]
@Consumption.valueHelpDefinition: [{
entity: { name: 'I_Status_VH', element: 'StatusCode' }
}]
Status;
}

Besser: Custom Value Help via CDS View

Erstelle neue Data Definition ZI_BOOK_STATUS_VH:

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Value Help für Buch-Status'
define view entity ZI_BOOK_STATUS_VH
as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T(
p_domain_name: 'ZBOOK_STATUS'
)
{
@ObjectModel.text.element: ['Description']
key domain_name as DomainName,
key value_position as ValuePosition,
@Semantics.language: true
key language as Language,
value_low as StatusCode,
text as Description
}

Alternativ: Einfachere Version ohne Domain

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Book Status Value Help'
define view entity ZI_BOOK_STATUS_VH
as select from I_Language
{
key 'N' as StatusCode,
cast( 'Neu' as abap.char(20) ) as StatusText
}
union select from I_Language
{
key 'R' as StatusCode,
cast( 'Lese ich gerade' as abap.char(20) ) as StatusText
}
union select from I_Language
{
key 'F' as StatusCode,
cast( 'Gelesen' as abap.char(20) ) as StatusText
}

Aktivieren und in Metadata Extension referenzieren:

@Consumption.valueHelpDefinition: [{
entity: { name: 'ZI_BOOK_STATUS_VH', element: 'StatusCode' }
}]
Status;

🎯 Was haben wir gelernt?

Actions

  • Definition: action startReading result [1] $self;
  • Feature Control: Buttons dynamisch verfügbar machen
  • Implementation: MODIFY ENTITIES zum Status ändern
  • UI: Actions in @UI.lineItem anzeigen

Validations

  • Wann: on save (vor Speichern) oder on modify (sofort)
  • Felder prüfen: ISBN-Format, Seitenzahl > 0
  • Fehler melden: failed + reported befüllen
  • UI: Felder rot markieren, Fehlermeldung anzeigen

Determinations

  • Wann: on modify { create; } (bei Anlage)
  • Zweck: Default-Werte setzen (z.B. Status = ‘N’)
  • Implementation: MODIFY ENTITIES zum Setzen

Feature Control

  • Dynamische Buttons: Basierend auf Daten (z.B. Status)
  • Method: get_instance_features
  • Logik: if_abap_behv=>fc-o-enabled vs. disabled

🚀 Nächste Schritte

In Teil 3: Best Practices:

  • Performance-Optimierung (SELECT Performance, EML Batching)
  • Error Handling & Logging
  • Unit Testing für RAP
  • Deployment & Versionierung
  • Häufige Fehler vermeiden

📚 Code-Übersicht

Neue/Geänderte Objekte:

  • ZBOOK_TAB (Status-Feld hinzugefügt)
  • ZI_BOOK (Status, StatusText)
  • ZC_BOOK (Status exposen)
  • ✅ BDEF Interface (Actions, Validations, Determinations)
  • ✅ BDEF Projection (Actions exposen)
  • ZBP_I_BOOK (Implementation)
  • ✅ Metadata Extension (UI-Annotations für Actions)

💬 Fragen?

  • Funktionieren die Actions?
  • Validations werden korrekt angezeigt?
  • Welche Business-Logik möchtest du noch hinzufügen?

Weiter mit Teil 3: Best Practices