This is a test message to test the length of the message box.
Login
ABAP RAP Custom Pattern
Erstellt von Software-Heroes

RAP - Custom Pattern (Verhalten)

42

In diesem Artikel erweitern wir das Custom Pattern in RAP um zusätzliche Daten und Verhalten. Damit können wir Mehrwerte in der Implementierung schaffen.

Werbung


In diesem Artikel erweitern wir unser RAP Objekt für die Software Komponenten um Verhalten und Ergänzen die aktuellen Daten um zusätzliche Informationen.

 

Einleitung

In einem der letzten Artikel hatten wir das Custom Pattern vorgestellt und wie es uns hilft verschiedene Funktionen und Schnittstellen als RAP Objekt zur Verfügung zu stellen. Dabei haben wir bereits die Datenbeschaffung über unsere wiederverwendbare Komponente implementiert und so weit Aufwand und Coding reduziert. In diesem Artikel schauen wir uns die Besonderheiten im Hinblick auf das Verhalten an.

 

Service 

In diesem Artikel müssen wir ein zusätzliches Service Binding anlegen, um die Änderungsoperationen aktivieren zu können. Wenn du dir das aktuelle Service Bindung (OData v4 für UI) anschaust, bekommst du eine Hinweismeldung.

 

Würden wir nun ein Verhalten implementieren, müssten wir dieses für OData v4 mit Draft ausstatten. Bereits bei der Anlage des Verhaltens und der Definition vom Draft, erhalten wir die Warnung, dass Draft nur teilweise unterstützt wird. Schauen wir uns dann das Service Binding an, erhalten wir die entsprechende Fehlermeldung.

 

Aus diesem Grund müssen wir für änderndes Verhalten einen OData v2 Bindung definieren. Dafür müssten wir allerdings auf die Draft Funktionalität verzichten und könnten die App auch nicht per Flexible Programming Model erweitern.

 

Daten

Im zweiten Schritt erweitern wir unseren Datenbestand um einige Informationen, die wir lokal im System persistieren wollen und zusammen mit den Daten in der App pflegen wollen.

 

Felder

Dazu ergänzen wir die folgenden Felder:

  • Staging - Damit wollen wir später abgrenzen, ob die Software Komponenten aus dem aktuellen System oder einem anderen System geladen werden. Zusammen mit der SWC bildet es den neuen Schlüssel.
  • Information - Hier können wir zusätzliche Informationen zur SWC speichern, da die Beschreibung an einer Komponente nur einmal gepflegt werden kann.
  • Team - Hier können wir ein zuständiges Team zuordnen, um später einen gezielten Ansprechpartner für die Entwicklung zu haben.
  • Applikation - Hier können wir die Software Komponente einer Applikation zuordnen, dies hilft verschiedene SWCs zusammenzufassen.

 

Tabelle

Dazu legen wir eine Datenbank Tabelle im System an, wo wir die zusätzlichen Informationen dann speichern werden. Für einige der Elemente legen wir auch gleich Datenelemente an.

@EndUserText.label : 'Custom Pattern Data'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zbs_dcp {
  key client  : abap.clnt not null;
  key staging : zbs_demo_dcp_staging not null;
  key sc_name : abap.char(18) not null;
  information : abap.char(200);
  team        : zbs_demo_dcp_team;
  application : zbs_demo_dcp_appl;
}

 

Custom Entity

Im letzten Schritt erweitern wir unsere Custom Entity um die zusätzlichen Felder und Schlüssel aus der Datenbank. Hier müssen wir neben den Feldern auch die Datentypen angeben, da wir kein Referenzobjekt haben, sondern die Felder direkt angelegt werden.

@EndUserText.label: 'Software Component'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_BS_DCP_SWC_QUERY'
@ObjectModel.semanticKey: [ 'staging', 'sc_name' ]
@UI.headerInfo: { typeName: 'Software Component',
                  typeNamePlural: 'Software Components',
                  title.value: 'sc_name',
                  description.value: 'descr' }
define root custom entity ZBS_R_DCPSoftwareComponent
{
      @UI.facet: [ { id: 'idRepositoryFields',
                     label: 'Technical Details',
                     position: 10,
                     type: #IDENTIFICATION_REFERENCE,
                     targetQualifier: 'REPO' },
                   { id: 'idTeam',
                     label: 'Team Information',
                     position: 20,
                     type: #IDENTIFICATION_REFERENCE,
                     targetQualifier: 'TEAM' } ]
                     
      @UI.identification: [ { position: 10, qualifier: 'REPO' } ]
      @UI.lineItem: [ { position: 10 } ]
      @UI.selectionField: [ { position: 10 } ]
  key staging          : zbs_demo_dcp_staging;

      @EndUserText.label: 'SWC'
      @UI.lineItem: [ { position: 20 } ]
      @UI.selectionField: [ { position: 20 } ]
  key sc_name          : abap.char(18);

      @EndUserText.label: 'Description'
      @UI.identification: [ { position: 20, qualifier: 'REPO' } ]
      @UI.lineItem: [ { position: 30 } ]
      descr            : abap.char(60);

      @EndUserText.label: 'Type'
      @UI.lineItem: [ { position: 40 } ]
      sc_type_descr    : abap.char(40);

      @EndUserText.label: 'Available'
      @UI.lineItem: [ { position: 50 } ]
      @UI.selectionField: [ { position: 50 } ]
      avail_on_inst    : abap_boolean;

      @EndUserText.label: 'Branch'
      @UI.identification: [ { position: 30, qualifier: 'REPO' } ]
      @UI.lineItem: [ { position: 60 } ]
      active_branch    : abap.char(40);

      @EndUserText.label: 'Info'
      @UI.identification: [ { position: 40, qualifier: 'TEAM' } ]
      @UI.multiLineText: true
      information      : abap.char(200);

      @UI.identification: [ { position: 50, qualifier: 'TEAM' } ]
      @UI.lineItem: [ { position: 70 } ]
      @UI.selectionField: [ { position: 60 } ]
      team             : zbs_demo_dcp_team;

      @UI.identification: [ { position: 60, qualifier: 'TEAM' } ]
      application      : zbs_demo_dcp_appl;
}

 

Dabei passen wir das UI leicht an und übernehmen die neuen Felder auf die Objektseite der App. Die Informationen möchten wir in einem eigenen Abschnitt darstellen und legen dafür bei den UI Annotationen eine eigene Facet an. Die Information soll in einer Textbox angezeigt werden, damit ein längerer Satz leicht gelesen werden kann.

 

Verhalten

Damit die Daten von der UI zur Datenbank kommen und gespeichert werden, legen wir auf der Custom Entity ein Verhalten an.

 

Definition

Hier können wir nur den "Unmanaged" Ansatz wählen. Eine Persistierung in Form einer Tabelle oder Draft Tabelle können wir in diesem Szenario nicht definieren. Wir möchten die Daten gern ändern, deshalb implementieren wir UPDATE und setzen die Felder aus dem Service aus lesend.

unmanaged implementation in class zbp_bs_dcp_softwarecomponent unique;
strict ( 2 );

define behavior for ZBS_R_DCPSoftwareComponent alias SWC
lock master
authorization master ( instance )
{
  update;

  field ( readonly )
  staging,
  sc_name,
  active_branch,
  avail_on_inst,
  descr,
  sc_type_descr;

  mapping for zbs_dcp
    {
      staging     = staging;
      sc_name     = sc_name;
      application = application;
      information = information;
      team        = team;
    }
}

 

Zum Abschluss definieren wir noch ein Mapping von der Custom Entity zur Datenbanktabelle. Im Grunde haben wir die Feldnamen hier identisch gewählt, wären diese allerdings komplett anders, hilft uns das Mapping beim Zuweisen der Daten.

 

Implementierung

Über den Quick Fix (STRG + 1) können wir die Klasse anlegen lassen. Dort werden bereits viele leere Methoden für uns angelegt. Für eine einfache Implementierung benötigen wir aber nicht alle Methoden. 

 

Puffer

Im ersten Schritt legen wir uns einen sehr einfachen Puffer als lokale Klasse an. Grundsätzlich kannst du auch über einen Singleton mit Instanziierung nachdenken oder ein Interface für die entsprechende Testbarkeit. In unserem Fall sollen einfach die aktuellen Updates in einer statischen Tabelle gesichert werden.

CLASS lcl_buffer DEFINITION.
  PUBLIC SECTION.
    TYPES swc_updates TYPE TABLE FOR UPDATE zbs_r_dcpsoftwarecomponentswc.

    CLASS-DATA updates TYPE swc_updates.
ENDCLASS.

 

Update

Im nächsten Schritt müssen wir nun auf die Änderung an den Daten reagieren. Dazu wird beim Speichern des Datensatzes die UPDATE Methode aufgerufen. Hier übernehmen wir die Entitäten in den Puffer, vorher kannst du auch noch inhaltliche Prüfungen durchführen.

METHOD update.
  INSERT LINES OF entities INTO TABLE lcl_buffer=>updates.
ENDMETHOD.  

 

Zum Abschluss implementieren wir die SAVE Methode und übernehmen den Puffer auf die Datenbank. Im ersten Schritt versuchen wir den Insert, da wir nicht wissen, ob die Daten bereits angelegt wurden. Im zweiten Schritt übernehmen wir die Daten per Update, dort kommt auch das Mapping aus der Verhaltensdefinition zum Einsatz. Dabei verwenden wir auch die %CONTROL Struktur, um nicht versorgte Felder nicht zu leeren.

METHOD save.
  LOOP AT lcl_buffer=>updates INTO DATA(update).
    INSERT zbs_dcp FROM @update MAPPING FROM ENTITY.
    IF sy-subrc <> 0.
      UPDATE zbs_dcp FROM @update INDICATORS SET STRUCTURE %control MAPPING FROM ENTITY.
    ENDIF.
  ENDLOOP.
ENDMETHOD.

 

Filterung

Zum Abschluss müssen wir nun noch die Query Klasse anpassen. Aktuell befinden sich Felder in der Entität die Remote nicht verfügbar sind, wenn es um das Filtern und Anzeigen der Daten geht. Weiterhin werden im Moment noch keine Daten abgeleitet und hinzugefügt, eine Filterung über diese Felder ist ebenfalls nicht möglich.

 

Löschen

Dazu können wir über die Konfiguration der wiederverwendbaren Komponente die Tabelle DELETE_FIELDS befüllen. Diese sorgt dafür, dass die Felder aus der Abgrenzung, Sortierung, den angeforderten Feldern und weiteren OData Bereichen entfernt werden. Würden wir diese Felder nicht entfernen, würde es beim Lesen zu einem Fehler kommen.

NEW zcl_bs_demo_custom_git( )->get_software_component(
  EXPORTING setting       = VALUE #( entity_name   = 'REPOSITORIES'
                                     request       = request
                                     delete_fields = VALUE #( ( `STAGING` )
                                                              ( `INFORMATION` )
                                                              ( `APPLICATION` )
                                                              ( `TEAM` ) ) )
  IMPORTING business_data = DATA(remote_data)
            count         = count ).

 

Anreichern

Im nächsten Schritt lesen wir die Daten der Datenbank und mappen sie zu den gelesenen Informationen aus dem OData Service. Damit erhalten wir alle Daten in unserer Tabelle, die wir dann ans Frontend übergeben können.

LOOP AT remote_data INTO DATA(remote).
  INSERT CORRESPONDING #( remote ) INTO TABLE business_data REFERENCE INTO DATA(line).

  SELECT SINGLE FROM zbs_dcp
    FIELDS information, application, team
    WHERE     staging = @test_stage
          AND sc_name = @line->sc_name
    INTO CORRESPONDING FIELDS OF @line->*.

  line->staging = test_stage.
ENDLOOP.

 

Anpassen

Im letzten Schritt müssen wir noch die Filterung und Sortierung aus der Anfrage übernehmen, damit auch die Felder der Datenbank berücksichtigt werden. Dazu verwenden wir unsere Komponente zur Anpassung der Daten, diese findest du unter den Code Snippets.

DATA(adjust_custom) = NEW zcl_bs_demo_adjust_data( ).

TRY.
    DATA(filter) = request->get_filter( )->get_as_ranges( ).
  CATCH cx_rap_query_filter_no_range.
    CLEAR filter.
ENDTRY.

adjust_custom->filter_data( EXPORTING it_filter = filter
                            CHANGING  ct_data   = software_components ).

adjust_custom->order_data( EXPORTING it_sort = request->get_sort_elements( )
                           CHANGING  ct_data = software_components ).

 

Test

Mit der letzten Anpassung können wir die Anwendung nun testen. Dabei laden wir alle verfügbaren Komponenten und ergänzen eine Komponente um die Zusatzinformationen. Im Anschluss verwenden wir den Filter auf der List Page und schränken auf das soeben gepflegte Produkt ein, um auch die Filter für die Datenbank zu testen.

 

Vollständiges Beispiel

Alle Beispiele der RAP Serie findest du in diesem GitHub Repository. Die Änderungen aus dem heutigen Artikel wurden über den folgenden Commit durchgeführt, dabei kannst du dir die Änderungen und neuen Objekte genauer anschauen.

 

Fazit

Du solltest nun auch Verhalten in der Anwendung definiert haben. Zusätzlich können wir nun weitere Informationen zur Software Komponente speichern und unsere Fiori Elements App mit nützlichen Features ausstatten.


Enthaltene Themen:
RAPBTPPatternCustom
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


RAP - Festwert-Filter

Kategorie - ABAP

Wie verwendest du Festwerte aus einer Domäne für einen Filter und passt diesen nach deinen Bedüfnissen in RAP an? Mehr dazu hier.

02.05.2025

RAP - Custom Pattern

Kategorie - ABAP

Wie funktioniert das Custom Pattern in der ABAP Entwicklung und für welche Szenarien kannst du es in der Entwicklung verwenden? Lass uns dazu ein Beispiel anschauen.

25.04.2025

RAP - Tree View (Löschverhalten)

Kategorie - ABAP

In diesem Artikel schauen wir uns das Verhalten beim Löschen von Knoten im Tree View mit RAP an. Dabei gibt es einige interessante Punkte zu beachten.

15.04.2025

RAP - Tree View

Kategorie - ABAP

Du möchtest eine Hierarchie in RAP ganz einfach darstellen? Wie das in ABAP Cloud funktioniert, erfährst du in diesem Artikel.

08.04.2025

RAP - Classic Pattern

Kategorie - ABAP

In diesem Artikel schauen wir uns das Classic Pattern an und gehen auf die Anwendungsfälle der Implementierung in ABAP Cloud ein.

25.03.2025