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

RAP - Custom Entity with Action

1433

How can you cleanly implement an action in a Custom Entity to update the UI und utilize EML? Let's take a closer look at the different steps.



The question for today's article came from the community of Abdullah ATAN. We'll discuss the details of the implementation and how the example works exactly.

 

Introduction

The question was also asked in the SAP Community, asking how an action can be implemented in a custom entity. Regular EML was also used, although there are a few things to consider to ensure it works properly. That's why we've created a small example that you can simply copy and try out.

 

Implementation

In this chapter, we'll look at the implementation and various details that are important in this case. You can find a general description of how to set up a custom entity in this article.

 

Custom Entity

In the first step, let's start with a custom entity that will map our structure and UI. In addition to defining the type, we also need to specify the query class that will provide the data.

@EndUserText.label: 'Custom Entity with Action'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_BS_DEMO_CUSTOM_ACTION_QRY'
define root custom entity ZBS_R_CusActEntityTP
{
  key my_key      : abap.char(15);
      description : abap.char(60);
      icon        : abap.sstring(250);
}

 

Query

In the next step, we implement the query that provides us with hard-coded data. With this, we want to simulate an API in the first step, perhaps providing us with the data via HTTP. We then need to prepare the amount of data for return using filters and sorting.

CLASS zcl_bs_demo_custom_action_qry DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_rap_query_provider.

    TYPES result_type TYPE STANDARD TABLE OF ZBS_R_CusActEntityTP WITH EMPTY KEY.

    METHODS get_dummy_data
      RETURNING VALUE(result) TYPE result_type.
ENDCLASS.


CLASS zcl_bs_demo_custom_action_qry IMPLEMENTATION.
  METHOD if_rap_query_provider~select.
    DATA result TYPE STANDARD TABLE OF ZBS_R_CusActEntityTP.
    DATA count  TYPE int8.

    result = get_dummy_data( ).

    NEW zcl_bs_demo_adjust_data( )->adjust_via_request( EXPORTING io_request = io_request
                                                        CHANGING  ct_data    = result
                                                                  cd_count   = count ).

    IF io_request->is_total_numb_of_rec_requested( ).
      io_response->set_total_number_of_records( count ).
    ENDIF.

    IF io_request->is_data_requested( ).
      io_response->set_data( result ).
    ENDIF.
  ENDMETHOD.


  METHOD get_dummy_data.
    RETURN VALUE #( ( my_key = 'TEST' description = 'First One' )
                    ( my_key = 'NOTHING' description = 'Second One' )
                    ( my_key = 'MORE' description = 'Third One' ) ).
  ENDMETHOD.
ENDCLASS.

 

Behavior

In the next step, we define a behavior on the custom entity to later implement in RAP. We want to use the update to update the status in a database, but only internally, within the RAP object. We define a normal action that also returns a result (itself). This is how we want to update the data record. We use the static action to reset all statuses.

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

define behavior for ZBS_R_CusActEntityTP alias CustomAction
lock master
authorization master ( instance )
{
  internal update;

  field ( readonly ) my_key;

  action myCustomAction result [1] $self;
  static action resetAllIcons;
}

 

UI

In order for the list and fields to be displayed, we still need UI annotations. We must define these directly in the custom entity. We also define the two actions in the UI. For the ICON field, we also define that it is displayed as an image.

@EndUserText.label: 'Custom Entity with Action'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_BS_DEMO_CUSTOM_ACTION_QRY'
define root custom entity ZBS_R_CusActEntityTP
{
      @UI.facet   : [{
        position  : 10,
        label     : 'General',
        type      : #IDENTIFICATION_REFERENCE
      }]

      @UI.lineItem: [{ position: 10 }, 
        { type: #FOR_ACTION, dataAction: 'myCustomAction', label: 'Custom Action' },
        { type: #FOR_ACTION, dataAction: 'resetAllIcons', label: 'Reset' } ]
      @UI.selectionField: [{ position: 10 }]
      @UI.identification: [{ position: 10 }]
      @EndUserText.label: 'Identifier'
  key my_key      : abap.char(15);

      @UI.lineItem: [{ position: 20, iconUrl: 'icon' }]
      @UI.identification: [{ position: 20 }]
      @EndUserText.label: 'Description'
      description : abap.char(60);

      @UI.lineItem: [{ position: 30 }]
      @EndUserText.label: 'Icon'
      @Semantics.imageUrl: true
      icon        : abap.sstring(250);
}

 

Implementation

Now let's move on to the actual implementation of the action in the behavior implementation. We would now implement the custom action as follows.

METHOD myCustomAction.
  READ ENTITIES OF ZBS_R_CusActEntityTP IN LOCAL MODE
       ENTITY CustomAction
       FROM CORRESPONDING #( keys )
       RESULT DATA(found_data).

  LOOP AT found_data INTO DATA(found).
    MODIFY ENTITIES OF ZBS_R_CusActEntityTP IN LOCAL MODE
           ENTITY CustomAction
           UPDATE FROM VALUE #( ( my_key               = found-my_key
                                  description          = found-description
                                  %control-description = if_abap_behv=>mk-on ) ).

    INSERT VALUE #( my_key = found-my_key
                    %param = found ) INTO TABLE result.
  ENDLOOP.
ENDMETHOD.

 

We read the selected data, then perform an update, and return the data so that the UI can update the record. However, for the EML to work, we must implement both methods. In the READ method, we receive the data and then filter the key to read the correct records. The implementation is very simple here.

METHOD read.
  DATA(all) = NEW zcl_bs_demo_custom_action_qry( )->get_dummy_data( ).

  LOOP AT keys INTO DATA(key).
    INSERT CORRESPONDING #( all[ my_key = key-my_key ] ) INTO TABLE result.
  ENDLOOP.
ENDMETHOD.

 

In the Update method, we actually only fill the buffer, since the actual action should only happen when saving.

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

 

In the SAVE method, we then perform the actual saving. In this case, we fill a table that contains the current status for the key. This allows us to save additional data to our "API" data and read it in the query class.

METHOD save.
  LOOP AT lcl_data_buffer=>updates INTO DATA(update).
    SELECT SINGLE FROM zbs_dmo_cstat
      FIELDS status
      WHERE my_key = @update-my_key
      INTO @DATA(found_status).

    IF sy-subrc <> 0.
      INSERT zbs_dmo_cstat FROM @( VALUE #( my_key = update-my_key
                                            status = 1 ) ).
    ELSE.
      UPDATE zbs_dmo_cstat FROM @( VALUE #( my_key = update-my_key
                                            status = found_status + 1 ) ).
    ENDIF.
  ENDLOOP.
ENDMETHOD.

 

Enrichment

In the final step, we enrich the result data in the query class. To do this, we read the status table and derive the corresponding icon that we want to display.

LOOP AT result REFERENCE INTO DATA(extend).
  SELECT SINGLE FROM zbs_dmo_cstat
    FIELDS status
    WHERE my_key = @extend->my_key
    INTO @DATA(found_status).

  IF sy-subrc <> 0.
    extend->icon = icons-none.
    CONTINUE.
  ENDIF.

  CASE found_status.
    WHEN 1.
      extend->icon = icons-one.
    WHEN 2.
      extend->icon = icons-two.
    WHEN 3.
      extend->icon = icons-three.
    WHEN OTHERS.
      extend->icon = icons-four.
  ENDCASE.
ENDLOOP.

 

Result

Here is an example of how the application works and behaves. We change the status of the individual lines and finally reset the status using another action.

 

Complete example

You can find the complete example from this article in our GitHub repository in the packageZBS_DEMO_RAP_CUSTOM_ACTION. In the commit you can take another look at the various changes that have been added to the repo.

 

Conclusion

When implementing an action in a custom entity, you can rely on the RAP framework, but you also have to handle it yourself. This is also clear in an unmanaged scenario. Since custom entities currently do not support a draft, an OData v2 service is sufficient in many cases if you also want to use the standard actions.


Included topics:
RAPBTPCustom EntityAction
Comments (4)



And further ...

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


RAP - Generator (from Scratch)

Category - ABAP

Does your development with RAP sometimes feel very slow? Generators do the work for you, building the actual stack and eliminating repetitive work.

08/05/2025

RAP - Action (Processing options)

Category - ABAP

How do you actually enable multi-select in RAP and control the various processing options? Here we'll look at the different options in the framework.

08/01/2025

RAP - API Pattern

Category - ABAP

In this article, we look at the API pattern for RAP and how you can use it flexibly in ABAP development to provide interfaces.

06/20/2025

RAP - Multiple filters and settings

Category - ABAP

What happens if we want to set several filters and fields as default in RAP and also need a default sorting?

05/16/2025

RAP - Message Length

Category - ABAP

Is your message truncated when outputting with RAP? Let's look at the problem and a solution.

05/13/2025