Skip to Content
Technical Articles

Fiori Elements List – Add and Implement Action Button

Recently, I worked on a requirement where we had to add an action button on Fiori Elements List Report. Looked at various options but nothing seems straightforward. UI5 Demo Kit suggests using UI5 Extensions which I wanted to avoid if I could. There are some annotations related to function import which looked promising but I couldn’t find any blog/help document on how to actually use them in CDS and implement in the OData service. Then I saw actions using BOPF, but this report which I was working didn’t have any business object attached to it.

Looking at some SAP standard apps I finally figured out how to achieve this without UI5 extension in Web-IDE.

For the purpose of this blog, I’ll use tried and tested flight data model. You should be able to replicate the solution in your system using the code I’ve provided.


In this blog I’ll show you how to add action button(s) on Fiori Element List Page without making UI5 extension in Web-IDE or using BOPF.

Setting the Scene

In this Fiori Elements List report app, I am going to display a list of flight connections (from table SPFLI) and I’ll show to how to add actions which we can use to cancel the flight and reverse the cancellation. In part 1 of the blog, we will see basic implementation. To keep blog manageable (for me) I am going to keep additional features like message handling, enable/disable action buttons in part 2 of the blog.

As a starting point, I have this Fiori Elements List App which is based on CDS View. I have exposed CDS View via SEGW using Data Source Reference.

CDS View: ZI_FlightConnections

@AbapCatalog.sqlViewName: 'ZISPFLI01'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Flight Connections'

define view ZI_FlightConnections
  as select from    spfli
    left outer join zspfli_act as CancellationInfo on  spfli.carrid = CancellationInfo.carrid
                                                   and spfli.connid = CancellationInfo.connid
  association [0..1] to U99_I_Airline as _Airline     on $projection.Airline = _Airline.Airline
  association [0..1] to U99_IAIRPORT  as _AirportFrom on $projection.AirportFrom = _AirportFrom.Airport
  association [0..1] to U99_IAIRPORT  as _AirportTo   on $projection.AirportTo = _AirportTo.Airport
  association [0..1] to S_CityAirport as _CityFrom    on $projection.CityFrom = _CityFrom.City
  association [0..1] to S_CityAirport as _CityTo      on $projection.CityTo = _CityTo.City

      @ObjectModel.foreignKey.association: '_Airline'
      @UI: { lineItem: [{ position: 10 }] , selectionField: [{ position: 10 }]}
  key spfli.carrid    as Airline,

      @UI.lineItem: [{ position: 20 }]
  key spfli.connid    as FlightConnection,

      @ObjectModel.foreignKey.association: '_CityTo'
      @UI: { lineItem: [{ position: 40 }] , selectionField: [{ position: 15 }]}
      spfli.cityfrom  as CityFrom,

      @ObjectModel.foreignKey.association: '_AirportFrom'
      @UI: { lineItem: [{ position: 50 }] , selectionField: [{ position: 20 }]}
      spfli.airpfrom  as AirportFrom,

      @ObjectModel.foreignKey.association: '_CityTo'
      @UI: { lineItem: [{ position: 70 }] , selectionField: [{ position: 25 }]}
      spfli.cityto    as CityTo,

      @ObjectModel.foreignKey.association: '_AirportTo'
      @UI: { lineItem: [{ position: 80 }] , selectionField: [{ position: 30 }]}
      spfli.airpto    as AirportTo,

      @UI.lineItem: [{ position: 90 }]
      spfli.deptime   as DepartureTime,

      @UI.lineItem: [{ position: 100 }]
      spfli.arrtime   as ArrivalTime,

      @UI.lineItem: [{ position: 120 }]
      @EndUserText.label: 'Cancelled On'

      @UI.lineItem: [{ position: 130 }]
      @EndUserText.label: 'Cancelled By'





If you are struggling to get to this point I would recommend you check Fiori Elements Wiki Page, section How to Guides for List Report. Make note that I have exposed CDS via SEGW and not directly using OData.pubish annotation. This is important because its DPC_EXT and MPC_EXT classes which we will be using to add actions and put ABAP code to process these actions.

Adding Action Button

We are going to add two action buttons ‘Cancel Flight‘ and ‘Keep Flight‘. On these actions we will set and reset values in fields Cancelled on and Cancelled by.

To add action button first we will have to add function import in OData service following which we will add annotation in CDS View to display buttons and link it to function import name.

Adding Function Import

In MPC_EXT class add following private method. This code add function import to OData service. It defines importing parameters, return parameter etc.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_ZIFLTCON_MPC_EXT->ADD_ACTION
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_ACTION_NAME                 TYPE        /IWBEP/MED_EXTERNAL_NAME
* +--------------------------------------------------------------------------------------</SIGNATURE>
  method add_action.

    data: lv_fc_fieldvalue type /iwbep/med_annotation_value,
          lo_complex_type  type ref to /iwbep/if_mgw_odata_cmplx_type,
          lo_prop          type ref to /iwbep/if_mgw_odata_property.

    data(lo_action) = model->create_action( iv_action_name ).
    "set return parameter
    lo_action->set_return_entity_type( 'ZI_FlightConnectionsType' ) .
    lo_action->set_return_entity_set( 'ZI_FlightConnections' ).

    lo_action->set_http_method( 'PUT' ).
    lo_action->set_return_multiplicity( /iwbep/if_mgw_med_odata_types=>gcs_cardinality-cardinality_1_1 ).
    "specify input parameters
    data(lo_parameter) = lo_action->create_input_parameter(
                                  iv_parameter_name = 'Airline'
                                  iv_abap_fieldname = 'AIRLINE' ).
    lo_parameter->/iwbep/if_mgw_odata_property~set_type_edm_string( ).
    lo_parameter->set_maxlength( iv_max_length = 3 ).

    data(lo_parameter1) = lo_action->create_input_parameter(
                                  iv_parameter_name = 'FlightConnection'
                                  iv_abap_fieldname = 'FLIGHTCONNECTION' ).
    lo_parameter1->/iwbep/if_mgw_odata_property~set_type_edm_string( ).
    lo_parameter1->set_maxlength( iv_max_length = 4 ).


Redefine DEFINE method in MPC_EXT class and make call to ADD_ACTION method to add function imports.

  method define.
    super->define( ) .
    add_action( iv_action_name = 'CancelFlight' ) .
    add_action( iv_action_name = 'KeepFlight' ) .

Following above changes check OData service metadata have function import added to it.

Change to CDS to Add Action Buttons.

In CDS add following annotation (UI.lineitem) before field Airline. With this annotation we are defining button (label), and asking system to call respective function import on these actions.

define view ZI_FlightConnections
  as select from    spfli

      @ObjectModel.foreignKey.association: '_Airline'
      @UI: { lineItem: [{ position: 10 } ,
                        { type: #FOR_ACTION, invocationGrouping: #CHANGE_SET, position: 0, dataAction: 'MPC_EXT:CancelFlight',  label: 'Cancel Flight' },
                        { type: #FOR_ACTION, invocationGrouping: #CHANGE_SET, position: 1, dataAction: 'MPC_EXT:KeepFlight'  ,  label: 'Keep Flight' }] ,
             selectionField: [{ position: 10 }]}
  key spfli.carrid                                   as Airline,


After above changes and activation you should be able to see action buttons on the list page.

Code to Process Action

Fiori Element List Report uses batch processing. To enable batch processing, in DPC_EXT class redefine method /iwbep/if_mgw_appl_srv_runtime~changeset_begin. Also, we will process all requests together, hence set cv_defer_mode = abap_true.

  method /iwbep/if_mgw_appl_srv_runtime~changeset_begin.
    cv_defer_mode = abap_true .

Next, redefine method /iwbep/if_mgw_appl_srv_runtime~changeset_process and put below code in it. Inline comment in code should give you clue one whats happening

  method /iwbep/if_mgw_appl_srv_runtime~changeset_process.
    data : lo_func_import_context type ref to /iwbep/if_mgw_req_func_import,
           lt_parameters          type /iwbep/t_mgw_name_value_pair,
           ls_flight_con_status   type zspfli_act,
           ls_result              type zcl_zifltcon_mpc_ext=>ts_zi_flightconnectionstype,
           ls_changeset_response  type /iwbep/if_mgw_appl_types=>ty_s_changeset_response.

    "read requests where operation is execute action (EA)
    loop at it_changeset_request assigning field-symbol(<lfs_changeset_request>)
            where operation_type = /iwbep/if_mgw_appl_types=>gcs_operation_type-execute_action.

      "find function name
      lo_func_import_context ?= <lfs_changeset_request>-request_context .
      data(lv_function_import_name) = lo_func_import_context->get_function_import_name( ) .

      if lv_function_import_name = 'CancelFlight' or lv_function_import_name = 'KeepFlight' .

        "read parameters
        lt_parameters = lo_func_import_context->get_parameters( ).
        ls_flight_con_status-carrid = lt_parameters[ name = 'AIRLINE' ]-value .
        ls_flight_con_status-connid = lt_parameters[ name = 'FLIGHTCONNECTION' ]-value .

        "set/reset values
        case lv_function_import_name.
          when 'CancelFlight'.
            ls_flight_con_status-cancelledby = sy-uname .
            ls_flight_con_status-cancelledon = sy-datum .
          when 'KeepFlight'.
            clear ls_flight_con_status-cancelledby  .
            clear ls_flight_con_status-cancelledon  .
        endcase .

        modify zspfli_act from ls_flight_con_status .

        "select new values
        "do you know - even if you haven't yet committed the changes,
        "system will return new data
        "search 'transaction isolation levels' to read more on this
        select single from zi_flightconnections fields *
          where  airline = @ls_flight_con_status-carrid
            and flightconnection = @ls_flight_con_status-connid
          into corresponding fields of @ls_result .

        "prepare response with operation number and respective data,
        "insert in CT_CHANGESET_RESPONSE
        ls_changeset_response-operation_no = <lfs_changeset_request>-operation_no .
             is_data = ls_result
             cr_data = ls_changeset_response-entity_data ).

        insert ls_changeset_response into table ct_changeset_response.
      endif .
    endloop .


After activation buttons should work


Action can be added to Fiori Element List Report using annotation, code in MPC_EXT and DPC_EXT classes.

Hope you found this blog useful.

As I mentioned earlier I couldn’t find way to add action using BOPF in List Report. If you have managed to do that then please share your experience.

What’s next:

I plan to write next part of this blog.

Action to download file e.g. download sales confirmation output on list of sales order.

You must be Logged on to comment or reply to a post.
  • Hello,

    Thanks for sharing, but I would like to know why you are using ABAP code to create your Function Import whereas SEGW has wizard to do it ?



    • Hi Joseph,

      Wizard option does not let you create function import on or using entities/entity types which are coming from ‘Data Source References’. As you can see from below screenshot I cannot select values for ‘Return Entity Set’, and the same thing happens for ‘Action for Entity Types’.

  • Hello,

    Very nice and usefull blog.

    I have done the same steps and my buttons are not active in the layout.

    I can see the function import in the metadata:

    and the code to add the button is the following:

    METHOD add_action.
        DATA: lv_fc_fieldvalue TYPE /iwbep/med_annotation_value,
              lo_complex_type  TYPE REF TO /iwbep/if_mgw_odata_cmplx_type,
              lo_prop          TYPE REF TO /iwbep/if_mgw_odata_property.
        DATA(lo_action) = model->create_action( iv_action_name ).
        lo_action->set_action_for( 'xLIONxC_REV_DeductionItemType' ).
    *    lo_action->set_disabled( EXPORTING iv_disabled = abap_false ).
        "set return parameter
        lo_action->set_return_entity_type( 'xLIONxC_REV_DeductionItemType' ) .
        lo_action->set_return_entity_set( 'xLIONxC_REV_DeductionItem' ).
        lo_action->set_http_method( 'PUT' ).
        lo_action->set_return_multiplicity( /iwbep/if_mgw_med_odata_types=>gcs_cardinality-cardinality_1_1 ).
        "specify input parameters
        DATA(lo_parameter) = lo_action->create_input_parameter(
                                      iv_parameter_name = 'DedDoc'
                                      iv_abap_fieldname = 'DEDOC' ).
        lo_parameter->/iwbep/if_mgw_odata_property~set_type_edm_string( ).
        lo_parameter->set_maxlength( iv_max_length = 10 ).
    in the CDS views:
    @ObjectModel.readOnly: true
    @EndUserText.label: 'Deduction Document'
     @UI: { identification.position: 10,
            lineItem: [{ position: 10 } ,
                            { type: #FOR_ACTION, position: 0, dataAction: 'MPC_EXT:REQBACKDATA', label: 'Request Backing Data' },
                            { type: #FOR_ACTION, position: 1, dataAction: 'MPC_EXT:AUTOMATCH',  label: 'Auto Match' },
                            { type: #FOR_ACTION, position: 2, dataAction: 'MPC_EXT:REJECTDISP', label: 'Reject Dispute' },
                            { type: #FOR_ACTION, position: 3, dataAction: 'MPC_EXT:WORKFLOW', label: 'Workflow' },
                            { type: #FOR_ACTION, position: 4, dataAction: 'MPC_EXT:DISPVALCONTR', label: 'Display Valid Contract' }
                            ] }        

    When I run the appication, i have some errors in the console related to the function import:


    Do you have any idea why the buttons are not active?

    Thanks in advance for your help.


    • Hi,

      I have recently noticed this issue in another system. Can you please try with below additional code and let me know if it works. I'll update blog soon to reflect this.

      In CDS add additional field of type boolean for each action, which would control whether action is available or not on that row. You can simply make it 'X'/true if it is going to be available on all rows.

      cast( 'X' as boole_d ) as IsActiveCancelFlight,
      cast ( 'X' as boole_d ) as IsActiveKeepFlight,

      Add following lines in method ADD_ACTION towards the end.

          "Is Action Active?
          concatenate 'IsActive' iv_action_name into data(lv_action_ac).
          data(lo_annotation) = lo_action->/iwbep/if_mgw_odata_annotatabl~create_annotation( 'sap' ).
          lo_annotation->add( iv_key = 'action-for' iv_value = 'ZI_FlightConnectionsType' ).
          lo_annotation = lo_action->/iwbep/if_mgw_odata_annotatabl~create_annotation( 'sap' ).
          lo_annotation->add( iv_key = 'applicable-path' iv_value = lv_action_ac ).
  • Hi Pawan,


    thanks you so much for this blog! It was a good starting point for me and I think I can give you some update from my side. Just in case you haven’t discovered yet. ?

    Maybe we can avoid redefinitions in MPC_EXT and DPC_EXT

    I found this online help

    It seems to be key to use something like

    dataAction: 'BOPF:FOO'

    instead of some MPC_EXT.  Of course, FOO is the action’s name in my simple case.

    This annotation created a new property in my OData service. The name of the property was “Foo_ac”.

    So far, so good. But it did not work out of the box. For some reason, the value was always false and my button was disabled.

    Using debugger I found the following call

            io_model                     = lo_model
            io_expand_root               = lo_expand_root
            io_request_obj               = lo_request_obj
            it_header                    = mt_headers
            iv_generate_sts              = lv_generate_sts
            iv_sts_are_ready_to_use      = lv_sts_are_ready_to_use
            cv_response_body             = cv_response_body
            ct_header                    = ct_headers
            cr_entityset                 = cr_entityset
            cr_deleted_entityset         = cr_deleted_entityset
            cs_response_context          = cs_response_context
            ct_inline_info               = ct_inline_info ).

    (Method: /iwbep/cl_mgw_abs_data->/iwbep/if_mgw_core_srv_runtime~read_entityset)

    In debugging mode I could set Foo_ac to ‘X’ in the dereferenced table cr_entityset and the button become enabled and was working nicely.

    I hope I have some time soon to investigate further.

    But I’m happy if someone else has an idea. 🙂

    Thanks again for your work.


    • Hi Meinrad,

      Thank you for your feedback.

      You are absolutely right, with BOPF the way of defining and implementing action is different than how I have done in the blog.

      Blog will however apply to use cases where you might not have BOPF( e.g transaction processing is not enabled in CDS).

      MPC_EXT and DPC_EXT classes are there to be redefined so if needed for a given scenario/requirement then we should redefine methods in these classes.

      On the topic of how to enable disable the action dynamically you might find this SAP Help useful.



  • /