Quite frequently I’m getting requests to create an ALV report where only the “header data” is displayed…but after clicking on a hotspot area in that ALV, another grid (ALV) is to be displayed with detailed information (displayed as a sidebar on the screen, not as a new, modal screen). And of course, this sidebar should be possible to be closed/hidden.
There is a way using the docking containers being dynamically resized/created/removed and this is what I’m going to show in the following lines of code.
I’ll start with the simplest part / base of the program: START-OF-SELECTION event
START-OF-SELECTION.
lcl_demo=>get_instance( )->execute( ).
I have created a screen 0100 where all the magic will happen. This screen contain NO elements and its flow is as follows:
PROCESS BEFORE OUTPUT.
MODULE pbo_0100.
*
PROCESS AFTER INPUT.
MODULE pai_0100.
The PAI and PBO modules functionality is implemented in my local class’s methods:
MODULE pbo_0100 OUTPUT.
lcl_demo=>get_instance( )->pbo_0100( ).
ENDMODULE.
MODULE pai_0100 INPUT.
lcl_demo=>get_instance( )->pai_0100( sy-ucomm ).
ENDMODULE.
Here comes the local class definition:
CLASS lcl_demo DEFINITION.
PUBLIC SECTION.
TYPES:
* Structure of the HEAD data (SFLIGHT)
* ...it contains an extra field to hold text of the BUTTON
* and an extra STYLE field
BEGIN OF ty_s_head,
action(20) TYPE c.
INCLUDE TYPE sflight.
TYPES:
style TYPE lvc_t_styl,
END OF ty_s_head,
ty_t_head TYPE TABLE OF ty_s_head WITH DEFAULT KEY,
* Details are of type SBOOK
ty_t_detail TYPE TABLE OF sbook WITH DEFAULT KEY.
METHODS:
execute,
pbo_0100,
pai_0100 IMPORTING iv_ucomm TYPE sy-ucomm.
* Signleton implementation to avoid global data
CLASS-METHODS:
get_instance RETURNING VALUE(ro_instance) TYPE REF TO lcl_demo.
PRIVATE SECTION.
DATA:
* Data to hold HEAD and DETAILS entries for each ALV
mt_head TYPE ty_t_head,
mt_detail TYPE ty_t_detail,
* Docking containers for HEAD and DETAILS
mo_dock_head TYPE REF TO cl_gui_docking_container,
mo_dock_detail TYPE REF TO cl_gui_docking_container,
* ALV grids for HEAD and DETAILS
mo_grid_head TYPE REF TO cl_gui_alv_grid,
mo_grid_detail TYPE REF TO cl_gui_alv_grid,
* Indicator whether details screen is displayed
mv_details_visible TYPE abap_bool.
METHODS:
* Data selection methods
select_head_data RETURNING VALUE(rt_data) TYPE ty_t_head,
select_item_data IMPORTING iv_carrid TYPE s_carr_id
iv_connid TYPE s_conn_id
iv_fldate TYPE s_date
RETURNING VALUE(rt_data) TYPE ty_t_detail,
* ALV display/close methods
display_head,
display_details,
close_details.
* helper methods
add_grid_buttons,
get_fcat_4_itab IMPORTING it_table TYPE ANY TABLE
RETURNING VALUE(rt_fcat) TYPE lvc_t_fcat,
* event handling methods
handle_head_button_click
FOR EVENT button_click OF cl_gui_alv_grid
IMPORTING es_col_id
es_row_no,
handle_detail_toolbar
FOR EVENT toolbar OF cl_gui_alv_grid
IMPORTING e_object
e_interactive,
handle_detail_user_command
FOR EVENT user_command OF cl_gui_alv_grid
IMPORTING e_ucomm.
* Singleton implementation to avoid global data
CLASS-DATA:
mo_instance TYPE REF TO lcl_demo.
ENDCLASS.
Now the funnier part…
CLASS lcl_demo IMPLEMENTATION.
To avoid declaring global data I implemented the singleton design pattern to be able to access the one and only instance of the LCL_DEMO:
METHOD get_instance.
IF mo_instance IS INITIAL.
CREATE OBJECT mo_instance.
ENDIF.
ro_instance = mo_instance.
ENDMETHOD.
If you remember the START-OF-SELECTION event, we called method execute of the LCL_DEMO instance where we select the HEAD data and then navigate to screen 0100:
METHOD execute.
me->mt_head = me->select_head_data( ).
CALL SCREEN 0100.
ENDMETHOD.
Selection of the HEAD data is quite simple:
METHOD select_head_data.
SELECT *
INTO CORRESPONDING FIELDS OF TABLE rt_data
FROM sflight.
ENDMETHOD.
PBO module of the screen 0100 calls method pbo_0100 of our LCL_DEMO class. Here we set the PFSTATUS, where I defined just three (usual) user commands BACK, LEAVE and CANCEL. Next we display the HEAD data in an ALV grid stretched over the whole screen
METHOD pbo_0100.
SET PF-STATUS 'PFSTATUS'.
SET TITLEBAR 'TITLEBAR'.
me->display_head( ).
ENDMETHOD.
In display_head method we
- create the docking container which will hold the ALV grid and we use the parameter extension = 10000 to stretch it over the whole screen
- We call method add_grid_buttons which will prepare the necessary structures so cells in one of the ALV columns is displayed as buttons
- We use a generic helper method get_fcat_4_itab which returns a field catalog for any given ITAB
- We register event handler for handling user clicks on a button
- We display the grid
If the grid was already shown (in previous PBO event), we just refresh the grid data
METHOD display_head.
DATA:
lt_fcat TYPE lvc_t_fcat,
ls_layout TYPE lvc_s_layo,
ls_variant TYPE disvariant,
ls_stable TYPE lvc_s_stbl.
IF me->mo_dock_head IS INITIAL.
CREATE OBJECT me->mo_dock_head
EXPORTING
extension = 10000
parent = cl_gui_container=>screen0
side = cl_gui_docking_container=>dock_at_left.
CREATE OBJECT me->mo_grid_head
EXPORTING
i_parent = me->mo_dock_head.
ls_layout-sel_mode = 'A'.
ls_layout-no_merging = 'X'. "No merging of cells
ls_layout-no_rowmark = 'X'.
* ALV title
ls_layout-grid_title = 'Split screen demo'.
" style field name used to identify the BUTTON column
ls_layout-stylefname = 'STYLE'.
me->add_grid_buttons( ).
* ALV Variant
ls_variant-report = sy-repid.
ls_variant-username = sy-uname.
lt_fcat = me->get_fcat_4_itab( me->mt_head ).
" set handler method for button click
SET HANDLER me->handle_head_button_click FOR me->mo_grid_head.
CALL METHOD mo_grid_head->set_table_for_first_display
EXPORTING
is_variant = ls_variant
i_save = 'A' " U,X,
i_default = 'X' " Layout can be pre-set
is_layout = ls_layout " Default layout
CHANGING
it_outtab = mt_head
it_fieldcatalog = lt_fcat.
ELSE.
* Keep cursor position by refresh
ls_stable-row = 'X'.
ls_stable-col = 'X'.
CALL METHOD mo_grid_head->refresh_table_display
EXPORTING
is_stable = ls_stable.
ENDIF.
ENDMETHOD.
Method add_grid_buttons just manipulates the STYLE field of our head struture and sets the text of button:
METHOD add_grid_buttons.
DATA:
ls_style TYPE lvc_s_styl.
LOOP AT me->mt_head ASSIGNING FIELD-SYMBOL().
* Add style entry to turn the column ACTION into a button
ls_style-fieldname = 'ACTION'.
ls_style-style = cl_gui_alv_grid=>mc_style_button +
cl_gui_alv_grid=>mc_style_enabled.
INSERT ls_style INTO TABLE -style.
* Set text for field 'ACTION' in all rows
-action = 'BOOKINGS'.
ENDLOOP.
ENDMETHOD.
Generic method get_fcat_4_itab which generates field catalog for any givn internal table can be very handy in many cases:
METHOD get_fcat_4_itab.
DATA:
lo_columns TYPE REF TO cl_salv_columns_table,
lo_aggregations TYPE REF TO cl_salv_aggregations,
lo_salv_table TYPE REF TO cl_salv_table,
lr_table TYPE REF TO data.
FIELD-SYMBOLS:
<table> TYPE STANDARD TABLE.
* create unprotected table from import data
CREATE DATA lr_table LIKE it_table.
ASSIGN lr_table->* TO <table>.
*...New ALV Instance ...............................................
TRY.
cl_salv_table=>factory(
EXPORTING
list_display = abap_false
IMPORTING
r_salv_table = lo_salv_table
CHANGING
t_table = <table> ).
CATCH cx_salv_msg. "#EC NO_HANDLER
ENDTRY.
lo_columns = lo_salv_table->get_columns( ).
lo_aggregations = lo_salv_table->get_aggregations( ).
rt_fcat =
cl_salv_controller_metadata=>get_lvc_fieldcatalog(
r_columns = lo_columns
r_aggregations = lo_aggregations ).
DELETE rt_fcat WHERE rollname = 'MANDT'.
ENDMETHOD.
Method handle_head_button_click is called every time user clicks on any button in the ALV grid. We handle only the clicks on column ACTION, but generally it’s possible to have more columns where cells are displayed as buttons
METHOD handle_head_button_click.
TRY.
DATA(ls_flight) = me->mt_head[ es_row_no-row_id ].
CATCH cx_sy_itab_line_not_found.
" no relevant record found
ENDTRY.
CASE es_col_id-fieldname.
WHEN 'ACTION'.
me->mt_detail = me->select_item_data(
iv_carrid = ls_flight-carrid
iv_connid = ls_flight-connid
iv_fldate = ls_flight-fldate ).
IF me->mv_details_visible = abap_false.
me->mv_details_visible = abap_true.
me->display_details( ).
ELSE.
me->mo_grid_detail->refresh_table_display( ).
ENDIF.
* WHEN OTHERS.
* ...
ENDCASE.
ENDMETHOD.
So far we have covered creation and display of the HEAD data.
But In the last described method we will start handling the DETAIL data.
In case user clicked on a button in the ACTIONcolumn, we select the appropriate DETAIL data (bookings for the selected flight) for the row where button was clicked (the key consists of the Carrier, Connection ID and Flight date). Then if the details grid is already displayed, we just refresh data inside, otherwise we need to resize the main grid with HEAD data and create new grid with DETAIL data.
METHOD select_item_data.
SELECT *
INTO TABLE rt_data
FROM sbook
WHERE carrid = iv_carrid
AND connid = iv_connid
AND fldate = iv_fldate.
ENDMETHOD.
The display_details method:
- Resizes the ALV grid with HEAD data
- Creates docking container for the DETAIL grid
- Creates new ALV instance to hold the DETAIL data
- Registers event handlers where we are able to add (and handle) an extra CLOSE button to the ALV toolbar which will serve to close/hide the detail ALV
- Displays the DETAIL grid
METHOD display_details.
***********************************
* Reduce extension of main ALV
***********************************
* get extension of the main ALV with head data
me->mo_dock_head->get_extension(
IMPORTING
extension = DATA(lv_extension)
).
* reduce extension
lv_extension = lv_extension - ( lv_extension / 3 ).
* reduce size of the main ALV by setting new extension
mo_dock_head->set_extension( lv_extension ).
***********************************
* Create side bar
***********************************
* create new container
CREATE OBJECT me->mo_dock_detail
EXPORTING
side = cl_gui_docking_container=>dock_at_left
extension = 10000.
* create alv grid object
CREATE OBJECT me->mo_grid_detail
EXPORTING
i_parent = me->mo_dock_detail.
* If everything ok, let's display data
IF me->mo_grid_detail IS NOT INITIAL.
* set handler method for toolbar
SET HANDLER me->handle_detail_toolbar FOR me->mo_grid_detail.
* set handler method for user command
SET HANDLER me->handle_detail_user_command FOR me->mo_grid_detail.
* show alv grid
me->mo_grid_detail->set_table_for_first_display(
EXPORTING
i_structure_name = 'SBOOK'
is_layout = VALUE lvc_s_layo( zebra = 'X'
cwidth_opt = 'X'
sel_mode = 'A' )
CHANGING
it_outtab = me->mt_detail ).
ENDIF.
ENDMETHOD.
Event handler method handle_detail_toolbar serves to create an extra button in the ALV toolbar:
METHOD handle_detail_toolbar.
DATA: ls_button TYPE stb_button.
* button definition
ls_button-function = 'DETAIL_CLOSE'.
ls_button-icon = icon_close.
ls_button-quickinfo = ''.
ls_button-text = ''.
* add button to toolbar
INSERT ls_button INTO e_object->mt_toolbar INDEX 1.
ENDMETHOD.
Event handler method handle_detail_user_command handles the pushed CLOSE button and calls a helper method which destroys the objects and do the necessary cleanup:
METHOD handle_detail_user_command.
CASE e_ucomm.
WHEN 'DETAIL_CLOSE'.
me->close_details( ).
WHEN OTHERS.
" others
ENDCASE.
ENDMETHOD.
Method close_details resizes the main alv with HEAD data so it’s stretched over the whole screen again and then destroys/cleans the DETAIL-related objects:
METHOD close_details.
me->mo_dock_head->set_extension( 10000 ).
me->mo_grid_detail->free( ).
me->mo_dock_detail->free( ).
CLEAR:
me->mo_grid_detail,
me->mo_dock_detail,
me->mv_details_visible,
me->mt_detail.
FREE:
me->mo_grid_detail,
me->mo_dock_detail.
ENDMETHOD.
If you managed to read until this point, say horray!
Because that’s all folks.
If you are interested in how it really look like in SAP GUI, here follows some screenshots from the application:
Initial screen (main ALV with HEAD data from SFLIGHT table)
Dynamically split screen after a button was pressed:
Please notice the CLOSE button in the right ALV – this is our user-defined toolbar button. After user press it, the DETAIL grid is closed and the HEAD grid is resized so it’s stretched over the whole page again.
it is a good tutoriual. but why should I refresh it again, can I do it when I click button ( Booking)
is it not better?
Hi Ibrahim,
sadly I don’t understand which refresh are you refering to…if you mean the HEAD grid, then yes, you don’t have to refresh the ALV because the content (data) is loaded only once at start and is not changing/is not reloaded during PBO/PAI. So yes, you can remove this ALV refresh.