Tuesday, May 31, 2016

How to manually refresh an ALV (CL_SALV_TABLE) with set events


SALV is beautiful class offering many useful functions including famous automatic setup of field catalog. Everyone tries to dig his own ALV tunnel to work with grids. SALV looks as an excellent option, sure. But almost everything has its own BUT… SALV is not an exception. Let’s start my REFRESH journey.

Note: There e.g. class cl_gui_alv_grid with different behavior.

1) SALV without events

Imagine, we have straight forward setup. One screen with ALV grid inside custom container, all the stuff is  loaded within PBO section.

TOP include


Some TOP include should contain something like:

CONSTANTS gc_alv_kbed_container_name TYPE LENGTH 11 VALUE 'CONTROL_ALV'.
DATAgcr_container   TYPE REF TO cl_gui_custom_container,
            gcr_alv_control TYPE REF TO lcl_kbed_alv_control.


PBO ALV loader


MODULE d0100_alv_init OUTPUT.

  IF gcr_container IS NOT BOUND.
    CREATE OBJECT gcr_container
      EXPORTING
        container_name gc_alv_kbed_container_name
      EXCEPTIONS
        others         1.
    IF sy-subrc NE 0.
      MESSAGE a001.
    ENDIF.

    CREATE OBJECT gcr_alv_control
      EXPORTING
        it_kbed                  = gt_kbed         " input global kbed table for ALV
        icr_container         gcr_container
        iv_dynnr                sy-dynnr.
    gcr_alv_control->display_output).
  ELSE.
    gcr_alv_control->refresh_alvit_kbed = gt_kbed ).
  ENDIF.

ENDMODULE.                    


The constructor of our custom class could look like e.g.

  CLASS lcl_kbed_alv_control IMPLEMENTATION.

    METHOD constructor.

      " get data for ALV
      me->set_datait_kbed ).

      " create ALV object by factory
      TRY.
          cl_salv_table=>factory(
            EXPORTING
              r_container  icr_container
            IMPORTING
              r_salv_table pvcr_alv
            CHANGING
              t_table      me->pvt_kbed_alv ).
        CATCH cx_salv_msg INTO pvcx_root.
          MESSAGE pvcx_root TYPE 'E'.
      ENDTRY.

    ENDMETHOD.                   

The Refresh method 

    METHOD refresh_alv.

      me->set_datait_kbed ).
      me->pvcr_alv->refreshrefresh_mode if_salv_c_refresh=>full ).
      cl_gui_cfw=>flush).

    ENDMETHOD.           

Everything works fine. Container and ALV object are created just once in PBO. Every additional screen load causes just refresh of itab inside the ALV grid. Every sort or filter is kept. So, there is no problem so far.

2) SALV with events

Now we need to add some events, e.g. cell double click actions, e.g. adding of a new function. So some handlers are now taken into action. 

      " Register Events
      pvcr_events 
pvcr_alv->get_event( ).
      
SET HANDLER me->on_link_click       FOR pvcr_events.
      
SET HANDLER me->on_cell_doubleclick FOR pvcr_events.
      
SET HANDLER me->on_added_function   FOR pvcr_events.

Now the game has changed. Our custom refresh methods is no longer working. Why?

We can set up a full refresh mode and there is still no effect? Even static method flush of cl_gui_cfw does not help.

      me->pvcr_alv->refreshrefresh_mode if_salv_c_refresh=>full ).
      cl_gui_cfw=>flush).

So what's wrong?

In debug we can observe the different behavior:

The first picture shows scenario without events 

Everything works fine, refresh takes place.


The second one shows scenario with events 

Ups, suddenly we are kicked away from the method. No actual refresh is done at all.



How to solve refresh in this case?

We can create ALV every time screen is loaded. So PBO module could look like:

  " Refresh screen elements
  IF gcr_container IS BOUND.
    FREE gcr_alv_control.
    gcr_container->free).
  ENDIF.

  CREATE OBJECT gcr_container
    EXPORTING
      container_name gc_alv_kbed_container_name
    EXCEPTIONS
      others         1.
  IF sy-subrc NE 0.
    MESSAGE a001.
  ENDIF.

  CREATE OBJECT gcr_alv_control
    EXPORTING
      it_kbed           ls_kbed[]         " input global kbed table for ALV
      icr_container   gcr_container
      iv_dynnr          sy-dynnr.

  gcr_alv_control->display_output).



That's easy solution, fine, but what about keeping filters, sorting, any status of ALV object? For this case above trick does not work.



Refresh ala Naimesh 

The below code is inspired by Naimesh Patel's article "Editable SALV model". The goal is to call actual refresh method of ALV grid object. 


We need a bit rebuild the display method of upper custom ALV object.

    METHOD display_output.
      me->set_columns).
      me->set_alv_func_tools).
      me->set_layout).
      me->set_toolbar).

      DATAlo_alv_mod TYPE REF TO cl_salv_model.

      lo_alv_mod ?= me->pvcr_alv.   
      CREATE OBJECT pvcr_salv_model.   
      pvcr_salv_model->get_model( io_model lo_alv_mod ).


      me->pvcr_alv->display).   " SALV display method
    ENDMETHOD.                  



Also custom refresh_alv method needs some tuning. The only difference in comparing with Naimesh solution is type of lo_full_adap class, I used cl_salv_grid_adapter instead of cl_salv_fullscreen_adapter, otherwise it fell into casting error.

    METHOD refresh_alv.

      DATAlo_grid   TYPE REF TO cl_gui_alv_grid,
            lo_full_adap TYPE REF TO cl_salv_grid_adapter"cl_salv_fullscreen_adapter,


      me->set_datait_kbed ).
      me->pvcr_salv_model->get_adapter).
      lo_full_adap ?= me->pvcr_salv_model->o_adapter.
      lo_grid lo_full_adap->get_grid).
      lo_grid->refresh_table_display).


    ENDMETHOD.               


Now everything is fine. We have just one SALV object in memory keeping all sorts, filters and any status. We are just populating a new data into the grid. Everything works even with events, starting new transactions on cell double click.


Just for illustration I added some sample code how custom ALV class could look like. I removed many things to keep things simple for now. Sorry, I did not make any complete example. It's just a light version of real coding. But together with Naimesh example it is possible to catch the idea.

*----------------------------------------------------------------------*
*  Define the Local class inheriting from the CL_SALV_MODEL_LIST
*  to get an access of the model, controller and adapter which inturn
*  provides the Grid Object
*----------------------------------------------------------------------*
  CLASS lcl_salv_model DEFINITION INHERITING FROM cl_salv_model_list.
    PUBLIC SECTION.
      DATAo_control TYPE REF TO cl_salv_controller_model,
            o_adapter TYPE REF TO cl_salv_adapter.
      METHODS:
         get_model
          IMPORTING
            io_model TYPE REF TO cl_salv_model,
         get_controller,
         get_adapter.
    PRIVATE SECTION.
      DATAlo_model TYPE REF TO cl_salv_model.
  ENDCLASS.                    "LCL_SALV_MODEL DEFINITION



*----------------------------------------------------------------------*
* LCL_SALV_MODEL implementation
*----------------------------------------------------------------------*
  CLASS lcl_salv_model IMPLEMENTATION.
    METHOD get_model.
*   save the model
      lo_model io_model.
    ENDMETHOD.
    METHOD get_controller.
*   save the controller
      o_control lo_model->r_controller.
    ENDMETHOD.
    METHOD get_adapter.
*   save the adapter from controller
      o_adapter ?= lo_model->r_controller->r_adapter.
    ENDMETHOD.
  ENDCLASS.                    "LCL_SALV_MODEL IMPLEMENTATION



*----------------------------------------------------------------------*
*       CLASS lcl_kbed_alv_control DEFINITION
*----------------------------------------------------------------------*
* Main ALV class controling KBED table display
*----------------------------------------------------------------------*
  CLASS lcl_kbed_alv_control DEFINITION.
*
    PUBLIC SECTION.

      METHODS:
*     load data
        constructor
          IMPORTING
            it_kbed           TYPE ltty_kbed " ALV data table
            icr_container     TYPE REF TO cl_gui_custom_container
            iv_dynnr          TYPE sydynnr,
        refresh_alv
          IMPORTING
            it_kbed           TYPE ltty_kbed, " ALV data table
*     generate ALV output grid
        display_output.

    PRIVATE SECTION.

      DATApvcr_alv          TYPE REF TO cl_salv_table,
            pvcr_salv_model   TYPE REF TO lcl_salv_model,
            pvt_kbed_alv         TYPE STANDARD TABLE OF lsty_kbed_alv,
            pvcx_root               TYPE REF TO cx_root,
            pvcr_columns         TYPE REF TO cl_salv_columns_table,
            pvcr_funcs              TYPE REF TO cl_salv_functions_list,
            pvcr_events            TYPE REF TO cl_salv_events_table,
            pvcr_display           TYPE REF TO cl_salv_display_settings.

      METHODS:
        add_toolbar_function
            IMPORTING
              iv_name    TYPE c
              iv_icon    TYPE string
              iv_text    TYPE c
              iv_tooltip TYPE c,
*       transform data
          set_data
            IMPORTING it_kbed   TYPE ltty_kbed,
          set_alv_func_tools,
          set_layout,
          set_toolbar,
          set_columns,
*       event's method
          on_link_click FOR EVENT link_click OF cl_salv_events_table IMPORTING row column" event for checkbox
          on_cell_doubleclick FOR EVENT double_click OF cl_salv_events_table IMPORTING row column" normal double click/ hotspot is not needed in this case
          on_added_function FOR EVENT added_function OF cl_salv_events_table IMPORTING e_salv_function.
  ENDCLASS.                    " lcl_kbed_alv_control DEFINITION