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

RAP - Custom Pattern (Behavior)

314

In this article, we extend the Custom Pattern in RAP with additional data and behavior, allowing us to create added value in the implementation.



In this article, we extend our RAP object for the software components with behavior and supplement the current data with additional information.

 

Introduction

In one of the last articles, we introduced the Custom Pattern and how it helps us provide various functions and interfaces as a RAP object. We have already implemented data retrieval via our reusable component, thus reducing the effort and coding required. In this article, we'll look at the specifics regarding behavior.

 

Service 

In this article, we need to create an additional service binding to enable change operations. If you look at the current service binding (OData v4 for UI), you'll get a warning message.

 

If we were to implement a behavior now, we would have to equip it with Draft for OData v4. When creating the behavior and defining the draft, we receive a warning that the draft is only partially supported. If we then look at the service binding, we receive the corresponding error message.

 

For this reason, we must define an OData v2 binding for changing behavior. However, to do so, we would have to forego the draft functionality and would not be able to expand the app using the Flexible Programming Model.

 

Data

In the second step, we expand our database with some information that we want to persist locally in the system and maintain together with the data in the app.

 

Fields

We will add the following fields:

  • Staging - We will use this later to determine whether the software components are loaded from the current system or another system. Together with the SWC, it forms the new key.
  • Information - Here we can store additional information about the SWC, since the description of a component can only be maintained once.
  • Team - Here we can assign a responsible team to ensure we have a specific contact person for development later.
  • Application - Here we can assign the software component to an application; this helps to summarize different SWCs.

 

Table

To do this, we create a database table in the system where we will then store the additional information. We will also create data elements for some of the elements.

@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

In the final step, we extend our custom entity with the additional fields and keys from the database. Here, we must specify the data types in addition to the fields, since we don't have a reference object; the fields are created directly.

@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;
}

 

We'll slightly adapt the UI and add the new fields to the app's object page. We want to display the information in a separate section and create a separate facet for this in the UI annotations. The information should be displayed in a text box so that a longer sentence can be easily read.

 

Behavior

To ensure that the data is sent from the UI to the database and saved, we'll create a behavior on the custom entity.

 

Definition

Here we can only define the "Unmanaged" Choose an approach. We cannot define persistence in the form of a table or draft table in this scenario. We would like to change the data, so we implement UPDATE and set the fields from the service to read.

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;
    }
}

 

Finally, we define a mapping from the custom entity to the database table. We've basically chosen the same field names here, but if they were completely different, the mapping would help us assign the data.

 

Implementation

We can create the class using the Quick Fix (CTRL + 1). Many empty methods are already created for us there. However, we don't need all of them for a simple implementation.

 

Buffer

In the first step, we create a very simple buffer as a local class. In principle, you can also consider a singleton with instantiation or an interface for the corresponding testability. In our case, we simply want to save the current updates in a static table.

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

    CLASS-DATA updates TYPE swc_updates.
ENDCLASS.

 

Update

In the next step, we need to react to the change in the data. To do this, the UPDATE method is called when the data record is saved. Here, we transfer the entities to the buffer; you can also perform content checks beforehand.

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

 

Finally, we implement the SAVE method and transfer the buffer to the database. In the first step, we attempt the insert, since we don't know whether the data has already been created. In the second step, we transfer the data via an update, which is where the mapping from the behavior definition comes into play. We also use the %CONTROL structure to avoid emptying unfilled fields.

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.

 

Filtering

Finally, we need to adjust the query class. Currently, there are fields in the entity that are not remotely available when it comes to filtering and displaying data. Furthermore, no data is currently being derived or added, and filtering using these fields is also not possible.

 

Deleting

To do this, we can populate the DELETE_FIELDS table via the reusable component configuration. This ensures that the fields are removed from the delimiter, sorting, requested fields, and other OData areas. If we didn't remove these fields, a reading error would occur.

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 ).

 

Enrich

In the next step, we read the data from the database and map it to the information read from the OData service. This gives us all the data in our table, which we can then pass to the frontend.

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.

 

Adapt

In the final step, we need to adopt the filtering and sorting from the query so that the database fields are also taken into account. To do this, we use our component for adapting the data, which you can find in the 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

With the final adjustment, we can now test the application. We load all available components and add the additional information to one component. We then use the filter on the list page and restrict it to the product we just maintained to also test the filters for the database.

 

Complete example

You can find all examples in the RAP series in this GitHub repository. The changes from today's article were implemented via the following Commit. You can take a closer look at the changes and new objects.

 

Conclusion

You should now have defined behavior in the application. Additionally, we can now store further information about the software component and equip our Fiori Elements app with useful features.


Included topics:
RAPBTPPatternCustom
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


RAP - Fixed Value Filter

Category - ABAP

How do you use fixed values from a domain for a filter and customize it to your needs in RAP? Learn more here.

05/02/2025

RAP - Custom Pattern

Category - ABAP

How does the custom pattern work in ABAP development, and for which scenarios can you use it? Let's look at an example.

04/25/2025

RAP - Tree View (Deletion behavior)

Category - ABAP

In this article, we'll look at the behavior of deleting nodes in the tree view using RAP. There are some interesting points to note.

04/15/2025

RAP - Tree View

Category - ABAP

Want to easily display a hierarchy in RAP? Learn how to do it in ABAP Cloud in this article.

04/08/2025

RAP - Classic Pattern

Category - ABAP

In this article, we look at the Classic Pattern and discuss the use cases of its implementation in ABAP Cloud.

03/25/2025