RAP Value Helps: Annotation-basierte Wertehilfen in ABAP Cloud

kategorie
RAP
Veröffentlicht
autor
Johannes

Value Helps (Wertehilfen) sind ein zentrales Element jeder Fiori-Anwendung. Sie ermöglichen Benutzern die komfortable Auswahl von Werten aus vordefinierten Listen. In RAP werden Value Helps deklarativ über CDS-Annotations definiert – ohne eine einzige Zeile UI-Code.

Grundkonzept der Value Helps

Value Helps in RAP basieren auf der @Consumption.valueHelpDefinition Annotation. Diese verknüpft ein Feld mit einer CDS-Entity, die als Werteliste dient.

Einfache Value Help

Das minimale Setup besteht aus der Annotation auf dem Zielfeld:

define view entity ZC_SalesOrder
as projection on ZI_SalesOrder
{
@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_Customer',
element: 'CustomerId'
}
}]
key SalesOrderId,
CustomerId,
CustomerName,
OrderDate
}

Diese Konfiguration zeigt automatisch eine Wertehilfe mit allen Kunden aus ZI_Customer, wenn der Benutzer das Feld CustomerId anklickt.

Struktur der Annotation

@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_ValueHelpEntity', -- Name der CDS-Entity
element: 'KeyField' -- Schlüsselfeld (optional)
},
qualifier: 'MyValueHelp', -- Eindeutiger Bezeichner
label: 'Kundenauswahl', -- Titel des Dialogs
useForValidation: true, -- Validierung aktivieren
additionalBinding: [{...}], -- Zusätzliche Feldzuordnungen
distinctValues: true -- Nur eindeutige Werte
}]

Value Help Entity Design

Die Entity, die als Value Help dient, sollte speziell dafür optimiert sein:

@EndUserText.label: 'Kunden Value Help'
@ObjectModel.resultSet.sizeCategory: #XS
@Search.searchable: true
define view entity ZI_CustomerVH
as select from zcustomer
{
@UI.hidden: true
key customer_id as CustomerId,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
customer_name as CustomerName,
@Search.defaultSearchElement: true
city as City,
country as Country,
@UI.hidden: true
blocked as IsBlocked
}
where blocked = ''

Best Practices für Value Help Entities

AspektEmpfehlung
Größe@ObjectModel.resultSet.sizeCategory: #XS für kleine Listen
Suche@Search.searchable für Typeahead
FilterWHERE-Klausel für ungültige Einträge
FelderNur relevante Felder exponieren

Unterschied: Einfache vs. Komplexe Value Helps

Einfache Value Help

Zeigt nur das Schlüsselfeld und optional einen Beschreibungstext:

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

Die Value Help Entity:

define view entity ZI_StatusVH
as select from zstatus
{
key status_code as StatusCode,
status_text as StatusText
}

Komplexe Value Help

Enthält mehrere Spalten, Filter und zusätzliche Rückgabefelder:

@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_MaterialVH',
element: 'MaterialNumber'
},
additionalBinding: [{
element: 'MaterialDescription',
localElement: 'MaterialText',
usage: #RESULT
}, {
element: 'MaterialGroup',
localElement: 'MaterialGroup',
usage: #RESULT
}, {
element: 'BaseUnit',
localElement: 'Unit',
usage: #RESULT
}]
}]
MaterialNumber,
MaterialText,
MaterialGroup,
Unit,

Value Help mit Filterbedingungen

Filter schränken die angezeigten Werte ein. Es gibt zwei Arten: statische und dynamische Filter.

Statischer Filter

Filtert die Value Help auf feste Werte:

@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_ProductVH',
element: 'ProductId'
},
additionalBinding: [{
element: 'ProductType',
localConstant: 'FINISHED',
usage: #FILTER
}]
}]
ProductId,

Diese Value Help zeigt nur Produkte mit ProductType = 'FINISHED'.

Dynamischer Filter (abhängig von anderen Feldern)

Der Filter basiert auf dem Wert eines anderen Feldes:

define view entity ZC_PurchaseOrder
as projection on ZI_PurchaseOrder
{
key PurchaseOrderId,
@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_VendorVH',
element: 'VendorId'
},
additionalBinding: [{
element: 'PurchasingOrg',
localElement: 'PurchasingOrg',
usage: #FILTER
}]
}]
VendorId,
VendorName,
@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_PurchOrgVH',
element: 'PurchasingOrg'
}
}]
PurchasingOrg
}

Hier werden nur Lieferanten angezeigt, die zur ausgewählten Einkaufsorganisation gehören.

Mehrere Filter kombinieren

@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_PlantVH',
element: 'Plant'
},
additionalBinding: [{
element: 'CompanyCode',
localElement: 'CompanyCode',
usage: #FILTER
}, {
element: 'SalesOrg',
localElement: 'SalesOrg',
usage: #FILTER
}, {
element: 'IsActive',
localConstant: 'X',
usage: #FILTER
}]
}]
Plant,

Cascading Value Helps (Abhängige Wertehilfen)

Cascading Value Helps sind untereinander abhängig – die Auswahl in einer Value Help beeinflusst die Optionen in einer anderen.

Beispiel: Land → Region → Stadt

define view entity ZC_Address
as projection on ZI_Address
{
key AddressId,
-- 1. Ebene: Land
@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_CountryVH',
element: 'CountryCode'
},
additionalBinding: [{
element: 'CountryName',
localElement: 'CountryName',
usage: #RESULT
}]
}]
CountryCode,
CountryName,
-- 2. Ebene: Region (gefiltert nach Land)
@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_RegionVH',
element: 'RegionCode'
},
additionalBinding: [{
element: 'CountryCode',
localElement: 'CountryCode',
usage: #FILTER
}, {
element: 'RegionName',
localElement: 'RegionName',
usage: #RESULT
}]
}]
RegionCode,
RegionName,
-- 3. Ebene: Stadt (gefiltert nach Region)
@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_CityVH',
element: 'CityCode'
},
additionalBinding: [{
element: 'RegionCode',
localElement: 'RegionCode',
usage: #FILTER
}, {
element: 'CityName',
localElement: 'CityName',
usage: #RESULT
}]
}]
CityCode,
CityName
}

Value Help Entities für Cascading

-- Länder
define view entity ZI_CountryVH
as select from zcountry
{
key country_code as CountryCode,
country_name as CountryName
}
-- Regionen (mit Fremdschlüssel auf Land)
define view entity ZI_RegionVH
as select from zregion
{
key region_code as RegionCode,
region_name as RegionName,
country_code as CountryCode -- Filterkriterium
}
-- Städte (mit Fremdschlüssel auf Region)
define view entity ZI_CityVH
as select from zcity
{
key city_code as CityCode,
city_name as CityName,
region_code as RegionCode -- Filterkriterium
}

Value Help mit zusätzlichen Ausgabefeldern

Zusätzliche Ausgabefelder übertragen automatisch Werte aus der Value Help in andere Felder der Entity.

Beispiel: Materialstammdaten

define view entity ZC_SalesOrderItem
as projection on ZI_SalesOrderItem
{
key SalesOrderId,
key ItemNumber,
@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_MaterialVH',
element: 'MaterialNumber'
},
additionalBinding: [
-- Rückgabefelder
{ element: 'MaterialDescription', localElement: 'MaterialText', usage: #RESULT },
{ element: 'BaseUnit', localElement: 'Unit', usage: #RESULT },
{ element: 'MaterialGroup', localElement: 'MatGroup', usage: #RESULT },
{ element: 'GrossWeight', localElement: 'Weight', usage: #RESULT },
-- Gleichzeitig als Filter
{ element: 'SalesOrg', localElement: 'SalesOrg', usage: #FILTER_AND_RESULT }
]
}]
MaterialNumber,
MaterialText,
Unit,
MatGroup,
Weight,
SalesOrg,
Quantity,
NetPrice
}

Usage-Optionen

WertBeschreibung
#RESULTWert wird zurückgegeben
#FILTERWert dient als Filter
#FILTER_AND_RESULTBeides kombiniert

Praktisches Beispiel: Kunde mit Adresse

@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_CustomerVH',
element: 'CustomerId'
},
additionalBinding: [{
element: 'CustomerName',
localElement: 'CustomerName',
usage: #RESULT
}, {
element: 'Street',
localElement: 'CustomerStreet',
usage: #RESULT
}, {
element: 'City',
localElement: 'CustomerCity',
usage: #RESULT
}, {
element: 'PostalCode',
localElement: 'CustomerPostalCode',
usage: #RESULT
}, {
element: 'Country',
localElement: 'CustomerCountry',
usage: #RESULT
}, {
element: 'PaymentTerms',
localElement: 'PaymentTerms',
usage: #RESULT
}]
}]
CustomerId,
CustomerName,
CustomerStreet,
CustomerCity,
CustomerPostalCode,
CustomerCountry,
PaymentTerms,

Mehrere Value Helps für ein Feld

Ein Feld kann mehrere alternative Value Helps haben:

@Consumption.valueHelpDefinition: [
{
entity: { name: 'ZI_CustomerVH', element: 'CustomerId' },
qualifier: 'AllCustomers',
label: 'Alle Kunden'
},
{
entity: { name: 'ZI_KeyAccountVH', element: 'CustomerId' },
qualifier: 'KeyAccounts',
label: 'Key Accounts',
additionalBinding: [{
element: 'IsKeyAccount',
localConstant: 'X',
usage: #FILTER
}]
},
{
entity: { name: 'ZI_RecentCustomerVH', element: 'CustomerId' },
qualifier: 'RecentCustomers',
label: 'Zuletzt verwendet'
}
]
CustomerId,

Der Benutzer kann zwischen den Value Helps wählen.

Validierung mit Value Helps

Die Annotation useForValidation prüft eingegebene Werte gegen die Value Help:

@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_CurrencyVH',
element: 'CurrencyCode'
},
useForValidation: true
}]
Currency,

Bei ungültigen Eingaben zeigt Fiori automatisch eine Fehlermeldung.

Validierung in der Behavior Definition

Zusätzlich zur UI-Validierung sollte auch Backend-seitig validiert werden:

validation validateCurrency on save { field Currency; }
METHOD validateCurrency.
READ ENTITIES OF zi_salesorder IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( Currency )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
SELECT currency_code FROM zcurrency
INTO TABLE @DATA(lt_valid_currencies).
LOOP AT lt_orders INTO DATA(ls_order).
IF NOT line_exists( lt_valid_currencies[
currency_code = ls_order-Currency ] ).
APPEND VALUE #(
%tky = ls_order-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Währung { ls_order-Currency } ist ungültig| )
%element-Currency = if_abap_behv=>mk-on
) TO reported-salesorder.
APPEND VALUE #( %tky = ls_order-%tky ) TO failed-salesorder.
ENDIF.
ENDLOOP.
ENDMETHOD.

Collective Value Helps

Für Felder, die in mehreren Views verwendet werden, können zentrale Value Helps definiert werden:

-- Zentrale Value Help Definition
@ObjectModel.usageType: {
sizeCategory: #S,
serviceQuality: #A,
dataClass: #CUSTOMIZING
}
define view entity ZI_CountryCollVH
as select from I_Country
{
key Country as CountryCode,
_Text.CountryName as CountryName
}

Diese kann dann in verschiedenen Views referenziert werden:

-- In View 1
@Consumption.valueHelpDefinition: [{
entity: { name: 'ZI_CountryCollVH', element: 'CountryCode' }
}]
ShipToCountry,
-- In View 2
@Consumption.valueHelpDefinition: [{
entity: { name: 'ZI_CountryCollVH', element: 'CountryCode' }
}]
BillToCountry,

Typische Fehler und Lösungen

Fehler 1: Value Help zeigt keine Daten

Symptom: Der Value Help Dialog öffnet sich, aber ist leer.

Ursache: Die Value Help Entity hat keine Daten oder einen falschen Filter.

Lösung:

-- Prüfen mit SE16/ADT
SELECT * FROM zi_customerVH.
-- Filter entfernen oder korrigieren
define view entity ZI_CustomerVH as select from zcustomer
{
key customer_id as CustomerId
}
-- WHERE-Klausel prüfen

Fehler 2: Zusätzliche Felder werden nicht gefüllt

Symptom: Das Hauptfeld wird gefüllt, aber additionalBinding-Felder bleiben leer.

Ursache: Feldnamen stimmen nicht überein oder usage: #RESULT fehlt.

Lösung:

additionalBinding: [{
element: 'CustomerName', -- Muss exakt dem Feldnamen in der VH Entity entsprechen
localElement: 'CustomerName', -- Muss exakt dem Feldnamen in der Projection entsprechen
usage: #RESULT -- Nicht vergessen!
}]

Fehler 3: Cascading funktioniert nicht

Symptom: Abhängige Value Help zeigt alle Werte, nicht nur gefilterte.

Ursache: Das Filterfeld fehlt in der Value Help Entity.

Lösung:

-- Die VH Entity muss das Filterfeld enthalten
define view entity ZI_RegionVH as select from zregion
{
key region_code as RegionCode,
region_name as RegionName,
country_code as CountryCode -- Dieses Feld wird zum Filtern benötigt
}

Fehler 4: Performance-Probleme

Symptom: Value Help lädt langsam oder verursacht Timeouts.

Ursache: Große Datenmenge ohne Paginierung oder fehlende Indizes.

Lösung:

-- Größenkategorie setzen
@ObjectModel.resultSet.sizeCategory: #XS -- <100 Einträge
@ObjectModel.resultSet.sizeCategory: #S -- <1000 Einträge
-- Suche optimieren für Typeahead
@Search.searchable: true
define view entity ZI_LargeDataVH
{
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
key KeyField,
@Search.defaultSearchElement: true
TextField
}

Fehler 5: Validierungsfehler bei gültigen Werten

Symptom: Die Eingabe wird als ungültig abgelehnt, obwohl sie in der Value Help existiert.

Ursache: Case-Sensitivity oder führende/nachfolgende Leerzeichen.

Lösung:

-- In der Value Help Entity
define view entity ZI_StatusVH as select from zstatus
{
key upper( status_code ) as StatusCode, -- Case-insensitive
status_text as StatusText
}

Fehler 6: Value Help öffnet sich nicht

Symptom: Kein Value Help Icon oder Dialog.

Ursache: Die Annotation ist auf dem falschen Feld oder die Entity existiert nicht.

Lösung:

-- Annotation muss auf dem editierbaren Feld sein
@Consumption.valueHelpDefinition: [{
entity: { name: 'ZI_CustomerVH', element: 'CustomerId' } -- Entity-Name prüfen
}]
CustomerId, -- Nicht auf berechneten oder virtuellen Feldern

Erweiterte Techniken

Distinct Values

Für Felder mit Duplikaten:

@Consumption.valueHelpDefinition: [{
entity: { name: 'ZI_OrderStatusVH', element: 'Status' },
distinctValues: true
}]
Status,

Text-Arrangement für Value Helps

@ObjectModel.text.element: ['StatusText']
@UI.textArrangement: #TEXT_FIRST
@Consumption.valueHelpDefinition: [{
entity: { name: 'ZI_StatusVH', element: 'StatusCode' }
}]
StatusCode,

Value Help mit Assoziation

@Consumption.valueHelpDefinition: [{
entity: { name: 'ZI_CustomerVH' },
association: '_Customer'
}]
@ObjectModel.foreignKey.association: '_Customer'
CustomerId,
_Customer

Zusammenfassung

FeatureAnnotation/Eigenschaft
Basis Value Help@Consumption.valueHelpDefinition
FilterungadditionalBinding mit usage: #FILTER
RückgabefelderadditionalBinding mit usage: #RESULT
CascadingKombination von #FILTER auf abhängigen Feldern
ValidierunguseForValidation: true
Mehrere VHArray mit verschiedenen qualifier
Performance@ObjectModel.resultSet.sizeCategory

Value Helps sind ein mächtiges Werkzeug für benutzerfreundliche Fiori-Anwendungen. Mit der richtigen Kombination aus CDS-Annotations lassen sich komplexe Szenarien wie abhängige Wertehilfen und automatische Feldübernahmen vollständig deklarativ umsetzen.

Verwandte Themen