Skip to Content

Introduction


Filtering a list output is a very frequent requirement in SAP. SAP CRM Web UI table views provide filtering without any coding. The framework takes care of that. Users can click on the column header to get a Context Menu where they can choose any of the filter values.

Users can also select Custom and provide a wild card based filtering.

Althoug there is one limitation. Users can’t filter for multiple values of one column.

In a GUI ALV output users can filter on multiple values for the required column as follows:

As part of this blog, I will provide a way of so that users can enter multiple filter values as following:

The table view has a button “Filter” on clicking whcih a popup comes up where user can select the field on which they want to apply filter. They have a “+” icon to add more filter criteria. The view has a similar look and feel as that of a search view.


Implementation Steps


Step 1: Create a Dynamic Query Object with the same structure as that of the table view

This step is required to create the popup. Since the popup is basically a search view, we need a Dynamic Query Object to implement the same.

Go to Transaction SE24 and create an Implmentation Class of the Custom Component. Give superclass as CL_WCF_GENIL_ABSTR_COMPONENT.

Go to Transaction SM34 and open View Cluster CRMVC_GIL_APPDEF.
Define the custom Component and assign the implementation class.

Define Custom Component Set and assign the component to it

Go to transaction GENIL_MODEL_BROWSER and edit the Component created

Right Click on the Root Objects and select Create Root Object

Give any suitable name and provide the structure of the table view as the key structure, attribute structure and create structure.

Right Click on the Dynamic Query Objects and select Create Dynamic Query Object.

Give any suitable name and provide the structure of the table view as the attribute structure. Mention the root object created earlier as the Root Object and Result Object.

Step 2: Create a Search View
Go to transaction BSP_WD_CMPWB and create a new BSP Component
Assign the Component Set created in Step 1 as Model in the Runtime Repository.
Create an empty view with one model node corresponding to the BOL Entity defined earlier as Dynamic Query Object.
Define attribute VALUE_HELP_TABLE of type BSP_WD_DROPDOWN_TABLE in the context node implementation class.
Go to the superclass of the implementation class of the view and change the superclass of that to CL_BSP_WD_ADVSEARCH_CONTROLLER.
Go to the implementation class of the context node and change the superclass to CL_BSP_WD_CONTEXT_NODE_ASP.
Go to the view layout and add the following code

<%@page language="abap" %>
<%@extension name="thtmlb" prefix="thtmlb" %>
<%@extension name="chtmlb" prefix="chtmlb" %>
<%@extension name="bsp" prefix="bsp" %>
<thtmlb:areaFrame id="outer" >
  <thtmlb:areaFrameHeader title                 = "Select Filter Critetria"
                          noBackToTopButton     = "TRUE"
                          noExcelDownloadButton = "TRUE"
                          noPersonalizeButton   = "TRUE"
                          noTabChainButton      = "TRUE"
                          noTableGraphicsButton = "TRUE" />
  <thtmlb:areaFrameBody>
<%--ZATAB0000BO is the name of the context node--%>
    <thtmlb:advancedSearch id                = "advs0"
                           fieldMetadata     = "<%= controller->GET_DQUERY_DEFINITIONS( ) %>"
                           header            = "<%= ZATAB0000BO->get_param_struct_name( ) %>"
                           fieldNames        = "<%= controller->GET_POSSIBLE_FIELDS( ) %>"
                           values            = "//ZATAB0000BO/PARAMETERS"
                           showMaxHits       = "false"
                           onEnter           = "SEARCH"
                           ajaxDeltaHandling = "false" />
    <br><br>
    <thtmlb:button id      = "FILTER"
                   onClick = "FILTER"
                   text    = "Filter" />
    <thtmlb:button id      = "CLEAR"
                   onClick = "CLEAR"
                   text    = "Clear Filter" />
  </thtmlb:areaFrameBody>
</thtmlb:areaFrame>

Create a configurationfor the view and select the fields that you want to allow the users to filter on.

Create two event handlers CLEAR and FILTER.
Add the following code to read the filter criteria and navigate back to the previous screen.

  METHOD eh_onfilter.
    DATA: lr_window        TYPE REF TO cl_bsp_wd_window,
          lr_query_service TYPE REF TO cl_crm_bol_dquery_service,
          lo_col           TYPE REF TO if_bol_bo_col.

    lr_query_service ?= me->typed_context->zatab0000bo->collection_wrapper->get_current( ).
    lr_query_service->delete_empty_selection_params( ).
    CALL METHOD lr_query_service->get_selection_params
      RECEIVING
        rv_result = lo_col.
    lr_window = me->view_manager->get_window_controller( ).
    lr_window->call_outbound_plug( iv_outbound_plug = 'FILTER' iv_data_collection = lo_col ).
  ENDMETHOD.

Add the following code to clear the filter criteria and navigate back to the previous screen.

  METHOD eh_onclear.
    DATA: lr_window    TYPE REF TO cl_bsp_wd_window,
          lo_col_class TYPE REF TO cl_crm_bol_bo_col,
          lo_col       TYPE REF TO if_bol_bo_col,
          lr_qs        TYPE REF TO cl_crm_bol_dquery_service.
    lr_qs ?= me->typed_context->zatab0000bo->collection_wrapper->get_current( ).
    IF lr_qs IS BOUND.
      lr_qs->clear_selection_param_values( ).
    ENDIF.
    CREATE OBJECT lo_col_class.
    lo_col ?= lo_col_class.
    lr_window = me->view_manager->get_window_controller( ).
    lr_window->call_outbound_plug( iv_outbound_plug = 'FILTER' iv_data_collection = lo_col ).
  ENDMETHOD.

Define context node in the component controller with the same BOL entity as that of the table view context node. This is required for the dropdown in the filter popup.

Redefine the DO_INIT_CONTEXT method

  METHOD do_init_context.
    DATA: lr_iterator TYPE REF TO if_bol_bo_col_iterator,
          lr_entity   TYPE REF TO if_bol_bo_property_access.
    DATA: lt_table  TYPE bsp_wd_dropdown_table,
          lt_fields TYPE STANDARD TABLE OF dfies.
    DATA: ls_table  TYPE bsp_wd_dropdown_line,
          ls_fields TYPE dfies.
    DATA: lv_attr  TYPE name_komp,
          lv_value TYPE string,
          lv_size  TYPE i.
    FIELD-SYMBOLS: <fs_value> TYPE string.
    CALL METHOD super->do_init_context.
    lr_iterator ?= me->typed_context->output->collection_wrapper->if_bol_bo_col~get_iterator( ).
    CALL FUNCTION 'DDIF_FIELDINFO_GET'
      EXPORTING
        tabname        = 'ZATAB0000BO_ATTR'
      TABLES
        dfies_tab      = lt_fields
      EXCEPTIONS
        not_found      = 1
        internal_error = 2
        OTHERS         = 3.
    IF sy-subrc <> 0.
* Implement suitable error handling here
    ENDIF.
    CALL METHOD lr_iterator->get_first
      RECEIVING
        rv_result = lr_entity.
    WHILE lr_entity IS BOUND.
      LOOP AT lt_fields INTO ls_fields.
        lv_attr = ls_fields-fieldname.
        CALL METHOD lr_entity->get_property_as_string
          EXPORTING
            iv_attr_name = lv_attr
          RECEIVING
            rv_result    = lv_value.
        ls_table-key = lv_attr.
        ls_table-value = lv_value.
        APPEND ls_table TO lt_table.
      ENDLOOP.
      CALL METHOD lr_iterator->get_next
        RECEIVING
          rv_result = lr_entity.
    ENDWHILE.
    SORT lt_table BY key value.
    DELETE ADJACENT DUPLICATES FROM lt_table COMPARING ALL FIELDS.
    me->typed_context->zatab0000bo->value_help_table = lt_table.
  ENDMETHOD.

Redefine the method GET_P_S_STRUCT to make the fields dropdown

  METHOD get_p_s_struct.
    CASE iv_property.
      WHEN if_bsp_wd_model_setter_getter=>fp_fieldtype.
        rv_value = cl_bsp_dlc_view_descriptor=>field_type_picklist.
    ENDCASE.
  ENDMETHOD.

Redefine the method GET_V_S_STRUCT to provide values in the dropdown

  METHOD get_v_s_struct.
    DATA: lr_dd TYPE REF TO cl_crm_uiu_ddlb.
    DATA: lt_tab TYPE bsp_wd_dropdown_table.
    DATA: lwa_val TYPE bsp_wd_dropdown_line,
          ls_tab  LIKE LINE OF lt_tab.
    LOOP AT value_help_table INTO lwa_val WHERE key = COMPONENT.
      ls_tab-key = lwa_val-value.
      ls_tab-value = lwa_val-value.
      APPEND ls_tab TO lt_tab.
    ENDLOOP.
    CREATE OBJECT lr_dd
      EXPORTING
        iv_source_type = 'T'.
    APPEND INITIAL LINE TO lt_tab.
    lr_dd->set_selection_table( it_selection_table = lt_tab ).
    rv_valuehelp_descriptor = lr_dd.
  ENDMETHOD.

Step 3: Create a Window
Create a window and add the context node for the same BOL Entity. Bind the view context node with window context node. Create an inbound plug DEFAULT and an outbound plug FILTER.

Add the following code in the inbound plug method. This method is implemented to populate the filter criteria set earlier in the session i.e. say user selects some filter criteria and filters the list and then again goes back to the pop up to select/deselect some filters then the previous filter criteria is set using this method.

  METHOD ip_default.
    DATA: lr_query_service TYPE REF TO cl_crm_bol_dquery_service,
          lr_service       TYPE REF TO if_bol_bo_property_access,
          lo_collection    TYPE REF TO if_bol_bo_col,
          lo_col_class     TYPE REF TO cl_crm_bol_bo_col,
          lr_entity        TYPE REF TO if_bol_bo_property_access.
    DATA: ls_param TYPE genilt_selection_parameter.
    IF iv_collection IS BOUND AND iv_collection->size( ) > 0.
      TRY.
          CALL METHOD cl_crm_bol_dquery_service=>get_instance
            EXPORTING
              iv_query_name = 'DataLoadMultipleFilter'
            RECEIVING
              rv_result     = lr_query_service.
        CATCH cx_crm_unsupported_object .
      ENDTRY.
      lr_entity ?= iv_collection->get_first( ).
      WHILE lr_entity IS BOUND.
        CALL METHOD lr_entity->get_properties
          IMPORTING
            es_attributes = ls_param.
        CALL METHOD lr_query_service->add_selection_param
          EXPORTING
            iv_attr_name = ls_param-attr_name
            iv_sign      = ls_param-sign
            iv_option    = ls_param-option
            iv_low       = ls_param-low
            iv_high      = ls_param-high.
        lr_entity ?= iv_collection->get_next( ).
      ENDWHILE.
      lr_service ?= lr_query_service.
      CREATE OBJECT lo_col_class.
      CALL METHOD lo_col_class->if_bol_bo_col~add
        EXPORTING
          iv_entity = lr_service.
      lo_collection ?= lo_col_class.
      me->typed_context->zatab0000bo->collection_wrapper->set_collection( lo_collection ).
    ENDIF.
  ENDMETHOD.

Add the following code to the outbound plug, to navigate away from the view and close the popup.

  METHOD op_filter.
    fire_outbound_plug( iv_outbound_plug = 'FILTER' iv_data_collection = iv_data_collection ).
  ENDMETHOD.

4. Create Interface View
Define context node in the component controller with the same BOL entity as that of the table view context node.

Bind this context node with the corresponding context node in the view.

Assign the view to the window and add the window as an interface view in the runtime repository.

5. Define Usage

Redefine the method WD_USAGE_INITIALIZE of the component controller of the table view component.

  METHOD wd_usage_initialize.
    CALL METHOD super->wd_usage_initialize
      EXPORTING
        iv_usage = iv_usage.
    CASE iv_usage->usage_name.
      WHEN 'SetFilterUsage'.
        TRY.
            iv_usage->bind_context_node( iv_controller_type  = cl_bsp_wd_controller=>co_type_component
                                       iv_target_node_name = 'OUTPUT'
                                       iv_node_2_bind      = 'OUTPUT' ).
          CATCH cx_root.
        ENDTRY.
    ENDCASE.
  ENDMETHOD.



6. Add the Filter Button
Define attribute FILTER_POPUP type ref to IF_BSP_WD_POPUP in the table view implementation class.
Define attribute GO_FILTER type ref to  IF_BOL_BO_COL in the table view implementaion class to hold the filter criteria.

Go to the table view and redefine the method IF_BSP_WD_DYN_BTN_CONTROL~GET_BUTTONS and add the following code. The button is enabled if the table view has entries in it.

  METHOD if_bsp_wd_dyn_btn_control~get_buttons.
    DATA: ls_button TYPE crmt_thtmlb_button.
    ls_button-id = 'FILTER'.
    ls_button-text = 'Filter'.
    ls_button-on_click = 'FILTER'.
    IF me->typed_context->zatab0000bo->collection_wrapper->if_bol_bo_col~size( ) > 0.
      ls_button-enabled = abap_true.
    ELSE.
      ls_button-enabled = abap_false.
    ENDIF.
    APPEND ls_button TO rt_result.
  ENDMETHOD.

Implement the event handler for event FILTER

  METHOD eh_onfilter.
    DATA: lo_col             TYPE REF TO if_bol_bo_col,
          lr_comp_controller TYPE REF TO zacl_class0000b9_impl.
    lo_col ?= me->typed_context->zatab0000bo->collection_wrapper->if_bol_bo_col~get_copy( ).

    IF filter_popup IS NOT BOUND.
      filter_popup = comp_controller->window_manager->create_popup(
                         iv_interface_view_name = 'ZDATA_LOAD_PGM/MultipleFilter' "#EC NOTEXT
                         iv_usage_name = 'SetFilterUsage'
                         iv_title = 'Set Filter' ).
    ENDIF.
    filter_popup->set_on_close_event( iv_view = me iv_event_name = 'SET_FILTER_CLOSE' ).
    filter_popup->set_display_mode( if_bsp_wd_popup=>c_display_mode_surrounded ).
    filter_popup->open( iv_collection = go_filter iv_inbound_plug = 'DEFAULT' ).

" To implement the dropdown in the filter popup we need to send the table data to the pop up 
" so the following code is added
    lr_comp_controller ?= me->comp_controller.
    lr_comp_controller->typed_context->output->collection_wrapper->set_collection( lo_col ).
  ENDMETHOD.

Define the on close event SET_FILTER_CLOSE

  METHOD eh_onset_filter_close.
    TYPES: BEGIN OF ty_filter,
             attr TYPE name_komp,
             val  TYPE string,
           END OF ty_filter.
    TYPES: BEGIN OF ty_attr,
             attr TYPE name_komp,
           END OF ty_attr.
    DATA: lo_col          TYPE REF TO if_bol_bo_col,
          lo_entity_col   TYPE REF TO if_bol_entity_col,
          lr_parent       TYPE REF TO cl_crm_bol_entity,
          lr_child        TYPE REF TO cl_crm_bol_entity,
          lr_entity       TYPE REF TO if_bol_bo_property_access,
          lo_new_col      TYPE REF TO cl_crm_bol_bo_col,
          lo_old_col      TYPE REF TO if_bol_bo_col,
          lr_iterator     TYPE REF TO if_bol_bo_col_iterator,
          lr_ent_iterator TYPE REF TO if_bol_entity_col_iterator,
          lr_filter       TYPE REF TO if_bol_bo_property_access.
    DATA: lv_plug      TYPE seocmpname,
          lv_attribute TYPE name_komp,
          lv_value     TYPE string,
          lv_index     TYPE i.
    DATA: ls_filter TYPE ty_filter,
          ls_attr   TYPE ty_attr.
    DATA: lt_filter TYPE STANDARD TABLE OF ty_filter,
          lt_attr   TYPE STANDARD TABLE OF ty_attr.

    CALL METHOD filter_popup->get_fired_outbound_plug
      RECEIVING
        rv_outbound_plug = lv_plug.
    IF lv_plug = 'FILTER'.
      CALL METHOD filter_popup->get_fired_op_data_collection
        RECEIVING
          rv_result = lo_col.
      go_filter = lo_col.
      IF lo_col->size( ) > 0.
        CALL METHOD lo_col->get_first
          RECEIVING
            rv_result = lr_filter.
        WHILE lr_filter IS BOUND.
          CALL METHOD lr_filter->get_property_as_value
            EXPORTING
              iv_attr_name = 'ATTR_NAME'
            IMPORTING
              ev_result    = lv_attribute.
          CALL METHOD lr_filter->get_property_as_value
            EXPORTING
              iv_attr_name = 'LOW'
            IMPORTING
              ev_result    = lv_value.
          CLEAR ls_filter.
          CLEAR ls_attr.
          ls_filter-attr = lv_attribute.
          ls_filter-val = lv_value.
          APPEND ls_filter TO lt_filter.
          ls_attr-attr = lv_attribute.
          APPEND ls_attr TO lt_attr.
          CALL METHOD lo_col->get_next
            RECEIVING
              rv_result = lr_filter.
        ENDWHILE.
        SORT lt_filter BY attr val.
        DELETE ADJACENT DUPLICATES FROM lt_filter.
        SORT lt_attr BY attr.
        DELETE ADJACENT DUPLICATES FROM lt_attr.

        CREATE OBJECT lo_new_col.
        LOOP AT lt_attr INTO ls_attr.
          lv_index = sy-tabix.
          CALL METHOD lo_new_col->if_bol_bo_col~get_copy
            RECEIVING
              rv_result = lo_old_col.
          lo_new_col->if_bol_bo_col~clear( ).
          LOOP AT lt_filter INTO ls_filter WHERE attr = ls_attr-attr.
            IF lv_index = 1.
              lr_iterator ?= me->typed_context->zatab0000bo->collection_wrapper->if_bol_bo_col~get_iterator( ).
            ELSE.
              CALL METHOD lo_old_col->get_iterator
                RECEIVING
                  rv_result = lr_iterator.
            ENDIF.
            lv_attribute = ls_filter-attr.
            lv_value = ls_filter-val.
            CALL METHOD lr_iterator->filter_by_property
              EXPORTING
                iv_attr_name = lv_attribute
                iv_value     = lv_value.

            IF lo_new_col IS BOUND.
              CALL METHOD lr_iterator->get_first
                RECEIVING
                  rv_result = lr_entity.
              WHILE lr_entity IS BOUND.
                CALL METHOD lo_new_col->if_bol_bo_col~add
                  EXPORTING
                    iv_entity = lr_entity.
                CALL METHOD lr_iterator->get_next
                  RECEIVING
                    rv_result = lr_entity.
              ENDWHILE.
            ENDIF.
          ENDLOOP.
        ENDLOOP.
      ELSE.
" Clear the filter, get the parent entity and 
        lr_parent ?= me->typed_context->zatab0000b4->collection_wrapper->get_current( ).
        TRY.
            CALL METHOD lr_parent->get_related_entities
              EXPORTING
                iv_relation_name = 'ZAET_CA_TO_ATAB0000BO'
              RECEIVING
                rv_result        = lo_entity_col.
          CATCH cx_crm_genil_model_error .
        ENDTRY.
      lo_col ?= lo_entity_col.
      me->typed_context->zatab0000bo->collection_wrapper->set_collection( lo_col ).
    ENDIF.
  ENDMETHOD.

So here is a working demo of the solution

To report this post you need to login first.

2 Comments

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

Leave a Reply