Technology Blog Posts by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
RakeshZore
Discoverer
0 Kudos
969

 

This blog covers RAP development using a custom entity, showcasing data with UI annotations, and surpassing the standard SAP data preview limit through the use of pagination.

Lets Start....

Step 1: Create a custom entity with the fields that need to be displayed or sent to the frontend.

@EndUserText.label: 'Custom entity for fetching data'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_IMPLEMENT_EXT' //This is the class where the data is handle and processed
@UI:{ headerInfo:{  typeName: 'RAP Learn',
                             typeNamePlural: 'RAP Learn',
                             title: { label: 'RAP Learn',
                                       type: #STANDARD,
                                       value: 'component'
                                     },
                             description: { value: 'obj_desc' },
                                     typeImageUrl: 'sap-icon://activity-items' //Show Icon image on header of object layout
                           }
               }
@Search.searchable: true //for Searching the data from the output which is also need to implement in the implemented class above 
define root custom entity ZCE_MAIN
{
      .facet   : [{
                      id: 'ID1',
                      purpose: #STANDARD,
                      type: #IDENTIFICATION_REFERENCE,
                      label : 'Component detail',
                      position: 10
                    },
                    {
                id: 'FG1',
                purpose: #STANDARD,
                type: #FIELDGROUP_REFERENCE,
                label : 'Validity Date',
                targetQualifier: 'FG1',
                position: 10
              }]
               : {  selectionField: [{ position: 10 }]}
      @Consumption.filter.mandatory: true
      @Consumption.valueHelpDefinition: [{
                            entity : { name: 'ZIV_F4MATNR' , element: 'matnr'   } ,
                            additionalBinding: [{ localElement: 'werks', element: 'werks' },
                                                         {  localElement: 'stlan' , element: 'stlan' },
                                                         {  localElement: 'stlal' , element: 'stlal' }]}]
      @Consumption.filter.selectionType: #SINGLE
  key matnr       : matnr;
               : {  selectionField: [{ position: 20 }]}
      @Consumption.filter.mandatory: true
      @Consumption.valueHelpDefinition: [{
                      entity : { name: 'ZIV_F4WERKS', element: 'werks' }
                                                         }]
      @Consumption.filter.selectionType: #SINGLE
  key werks       : werks_d;
               : {  selectionField: [{ position: 30 }]}
      @Consumption.filter.mandatory: true
      @Consumption.valueHelpDefinition: [{
                entity : { name: 'ZIV_F4STLAN' , element: 'stlan' }
                                                   }]
      @Consumption.filter.selectionType: #SINGLE
  key stlan       : stlan;
               : {  selectionField: [{ position: 40 }]}
      @Consumption.filter.mandatory: true
      @Consumption.filter.selectionType: #SINGLE
  key stlal       : stlal;
               : { lineItem: [{ position: 10 }], identification: [{ position: 10 }]}
      @EndUserText.label: 'Level'
  key bom_level   : abap.dec( 2, 0 );
               : { lineItem: [{ position: 20 }], identification: [{ position: 20 }]}
      @EndUserText.label: 'Component'
      .defaultSearchElement: true
  key component   : idnrk;
               : {  selectionField: [{ position: 50 }]}
      @Consumption.filter.mandatory: true
      @Consumption.filter.selectionType: #SINGLE
      @EndUserText.label: 'Date'
      zdate       : abap.dats;
               : { lineItem: [{ position: 30, cssDefault: { width: '13rem' }  }], identification: [{ position: 30 }]}
      @EndUserText.label: 'Object Description'
      .defaultSearchElement: true
      obj_desc    : maktx;
               : { lineItem: [{ position: 35, cssDefault: { width: '8rem' } }], identification: [{ position: 35 }]}
      @EndUserText.label: 'Quantity'
      comp_qty    : kmpmg_bi;
               : { lineItem: [{ position: 45 }], identification: [{ position: 45 }]}
      @EndUserText.label: 'Unit'
      comp_unit   : kmpme;
               : { lineItem: [{ position: 55 }], fieldGroup: [{ position: 10 , qualifier: 'FG1', label: 'Valid From' }]}
      @EndUserText.label: 'Valid from'
      valid_from  : datuv_bi;
               : { lineItem: [{ position: 65 }], fieldGroup: [{ position: 11 , qualifier: 'FG1', label: 'Valid To' }]}
      @EndUserText.label: 'Valid to'
      valid_to    : datub_bi;
}


While using the RAP framework, metadata extensions cannot be created for custom entities. Therefore, annotations must be directly included in the custom entity code itself for UI purposes.

Step 2 : Create an implementation class for the custom entity mentioned above and name it ZCL_IMPLEMENT_EXT or use the custom name specified in the second line of the code.

CLASS zcl_implement_ext DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC
  SHARED MEMORY ENABLED .

  PUBLIC SECTION.
    DATA : et_final TYPE TABLE OF zce_main.
    INTERFACES if_rap_query_provider .

  PROTECTED SECTION.
  PRIVATE SECTION.

ENDCLASS.



CLASS ZCL_IMPLEMENT_EXT IMPLEMENTATION.

  METHOD if_rap_query_provider~select.
    CASE io_request->get_entity_id( ).
      WHEN 'ZCE_MAIN'.
        TRY.
            DATA(lv_skip) = io_request->get_paging( )->get_offset( ). "gets the skip part from URL which means number of data to be skipped and default is 0 but later on it gets add by 20... till last data not get fetched
            DATA(lv_top) = io_request->get_paging( )->get_page_size( ). "gets the top part from URL which means number of data required and default is 20
            DATA(lt_filter) = io_request->get_filter( )->get_as_ranges( ). "gets the input value which is either pass in our filters or when click on single data line item
           
          CATCH cx_rap_query_filter_no_range.
            "handle exception
        ENDTRY.

        IF lv_top < 0.
*********** Start Logic
"Put logic for single data fetch in which it will trigger when you are going for object page while clicking on single item from display table
"Take inputs from LT_filter table for sending that particular data click 
*********** End Logic
            IF io_request->is_total_numb_of_rec_requested( ).
              io_response->set_total_number_of_records( lines( et_final ) ).
            ENDIF.
            IF io_request->is_data_requested(  ).
              io_response->set_data( et_final ).
            ENDIF.
        ELSE.
*********** Start Logic
"Put logic for actual multiple data fetch in which it will trigger when you click go button or any sort of actions like search, sort, excel download....
"Take inputs from LT_filter table for required data fetch 
*********** End Logic
          ENDIF.
********** Searching Logic
        DATA(lv_search) = io_request->get_search_expression( ). "gets the value from Search field in the UI
          IF lv_search IS NOT INITIAL.
            DATA lt_search LIKE et_final.

            lv_search = |*{ lv_search }*|.
            lt_search = VALUE #( BASE lt_search
                          FOR ls_f IN et_final
                          WHERE ( field1 CP lv_search OR  "Put required field to be search instead of field1,field2.....
                                  field2 CP lv_search  OR
                                  field3 cp lv_search OR
                                  field4 cp lv_search OR                                                                
                                                       ( ls_f ) ).
            et_final = lt_search.
          ENDIF.
***********End of Searching logic
***********Sorting logic
        DATA(lt_sort_elements) = io_request->get_sort_elements( ). "gets the detail table of ascending and descending of field we wants to be sorted from UI
          IF lt_sort_elements IS NOT INITIAL.
            LOOP AT lt_sort_elements INTO DATA(ls_sort).
              IF ls_sort-descending <> 'X'.
                SORT et_final BY (ls_sort-element_name) ASCENDING.
              ELSE.
                SORT et_final BY (ls_sort-element_name) DESCENDING.
              ENDIF.
            ENDLOOP.
          ENDIF.
**********End of sorting logic
**********Paging- logic for loading more data wrt Standard set amount of data
          IF lv_top IS NOT INITIAL OR lv_skip IS NOT INITIAL.
            /iwbep/cl_mgw_data_util=>paging( EXPORTING is_paging = VALUE #( top  = lv_top
                                                                            skip = lv_skip )
                                             CHANGING  ct_data   = et_final ).
          ENDIF.
*********End of Pagination

          IF io_request->is_total_numb_of_rec_requested( ).
            io_response->set_total_number_of_records( lines( et_final ) ). "set number of initial data to display overall
          ENDIF.

          IF io_request->is_data_requested(  ).
            io_response->set_data( et_final ). "Set data into the entity
          ENDIF.

        ENDIF.
    ENDCASE.
  ENDMETHOD.

ENDCLASS.


The aforementioned class plays a crucial role as it encapsulates the entire logic for data processing, including searching, sorting, and most importantly, setting data for our custom entity.

Step 3 : Create Service Definition of the Custom entity 

To create a service definition, simply right-click the custom entity in the Project Explorer, click 'New Service Definition', fill in the required inputs, and your service definition will be created.

@EndUserText.label: 'Service definition'
define service ZSD_MAIN {
  expose ZCE_MAIN;
  expose ZIV_F4MATNR; "This is for ValueHelp Definition, and it's okay not to expose it.
  expose ZIV_F4WERKS;
  expose ZIV_F4STLAN;
}


Let's take a look at one of the interface views of the ValueHelpDefinition.

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Interface view for ValueHelpDefinition - MATNR'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
define view entity ZIV_F4MATNR
  as select distinct from mast
    left outer join       makt on makt.matnr = mast.matnr
{
         : {  selectionField: [{ position: 10 }] , lineItem: [{ position: 10 }]}
      @EndUserText.label: 'Material No'
  key mast.matnr,
      @Consumption.filter.hidden: true
  key mast.werks,
      @Consumption.filter.hidden: true
  key mast.stlan,
      @Consumption.filter.hidden: true
  key mast.stlal,
      @Consumption.filter.hidden: true
         : {   lineItem: [{ position: 20 }]}
      @EndUserText.label: 'Material Desc'
      makt.maktx
}


Step 4 : Create a Service Binding for the aforementioned service definition.

To create a service binding, simply right-click the created service definition in the Project Explorer, click 'New Service Binding', fill in the required inputs, and your service binding will be created. While creating the Service Binding, I selected OData V2 - UI. Then, activate and publish the service. To view the UI output, you can use the Preview option in Eclipse to see the output as shown in the image below.

 

Screenshot 2025-10-02 211818.png

In conclusion, using RAP with custom entities and pagination facilitates efficient data handling. With UI annotations, you can easily preview and refine the user interface. We hope this guide has been helpful. Stay tuned for more tips!

 

Note: This program is created for a remote system. For cloud environments, you can use a service consumption model or access data through a remote system API in the implementation class.