Tuesday, September 29, 2009

Web Dynpro: Displaying all rows of a table for printing (print view)

We have a scenario wherein the application shows a basket of services/materials that the user has added for saving. The basket by default shows only 5 rows and it has the pagination buttons at the bottom. If the user adds more than 5 items and he wants to do a print of the page then all the items in the basket will not be shown. So we decided to create another Web Dynpro component that will just show the contents of a table so that the user can print all the data in the basket if he wishes to. This component will have to be generic one as we want all basked based applications to use this functionality. When transferring the internal table from one component to the other we will also send the information on the UI of the columns as the columns might have different caption than what is given by the data dictionary. There are also some columns which will have a dropdown by key text editor for that we need to send the value set for the element in the context.

Here is how the basket will look like with a print button on top of the table, the table has 9 rows but is showing only 5 rows at a time:

basket

The print basket view will show all the rows of the basket:

prinit view

Let’s see how we achieved this. To transfer data across the two components we created a class that contains the table data. We have called this class ZCL_BASKET.

The attributes in the class are as follows:

T_COLUMNS

Instance Attribute

Private

Type

CL_WD_TABLE_COLUMN=>TT_TABLE_COLUMN

Columns

T_VALUE_SET

Instance Attribute

Private

Type

ZRC_BASKET_VALUE_SET_T

Value set for drop down

O_UIBASKET

Instance Attribute

Private

Type Ref To

CL_WD_TABLE

UI Basket

O_BASKET_TYPE

Instance Attribute

Private

Type Ref To

CL_ABAP_STRUCTDESCR

Basket type

DESCRIPTION

Instance Attribute

Private

Type

STRING

Description

BASKET

Instance Attribute

Private

Type Ref To

DATA

Basket

The methods defined are:

GET_VALUE_SET

Instance Method

Public

Returns the value set

REPLACE_DROPDOWN_TO_TEXT

Instance Method

Private

GET_COLUMNS

Instance Method

Public

Get columns

GET_UIBASKET

Instance Method

Public

Get UI Basket

CONSTRUCTOR

Instance Method

Public

CONSTRUCTOR

GET_BASKET

Instance Method

Public

GET_BASKET_TYPE

Instance Method

Public

GET_DESCRIPTION

Instance Method

Public

The interesting method for us is the constructor that:

  1. gets the data in it’s private attributes.
  2. gets the RTTI value of the table.
  3. Save the column UI for the basket to be used in the print view

image

METHOD constructor.
  DATA l_o_tabledescr TYPE REF TO cl_abap_tabledescr.
  FIELD-SYMBOLS <l_basket> TYPE table.
  o_uibasket = i_o_uibasket.
  t_columns[] = i_t_columns[].
  t_value_set[] = i_t_value_set[].
  l_o_tabledescr ?= cl_abap_tabledescr=>describe_by_data( i_t_basket ).
  o_basket_type ?= l_o_tabledescr->get_table_line_type( ).
  description = i_description.
  CREATE DATA basket TYPE HANDLE l_o_tabledescr.
  ASSIGN basket->* TO <l_basket>.
  <l_basket> = i_t_basket[].
ENDMETHOD.

We have a Web Dynpro component ZWDRC_PRINT_BASKET which has a view V_PRINT_BASKET embedded into the window ZWDRC_PRINT_BASKET. The print basket component is of the following structure:

image

The inbound plug FROM_ANOTHER_VIEW in the window ZWDRC_PRINT_BASKET is the one to be used as standard and not a startup type because startup plug does not take parameters of type reference. This is the inbound plug that will be used by other calling components. Code for the the plug is:

METHOD handlefrom_another_view .
  wd_comp_controller->o_basket = i_o_basket.
ENDMETHOD.

The parameters defined are:

image

Details of the view V_PRINT_BASKET is:

image

The layout is empty as we plan to create elements dynamically in the WDDOMODIFYVIEW method.

From the method WDDOMODIFYVIEW call to is made to the PRINT_BASKET method which will add the dynamic table with data from the class ZCL_BASKET. I have referenced the code from this blog posting for creating dynamic table.

image

METHOD print_basket .
  DATA: group TYPE REF TO cl_wd_group , "for dynamic group ui element.
        capt_gr TYPE REF TO cl_wd_caption , "caption for group_2
        new_tab TYPE REF TO cl_wd_table ,
        rootelem TYPE REF TO if_wd_view_element , "to get root element
        rootnode TYPE REF TO cl_wd_transparent_container , "to store root “node
        dyn_node TYPE REF TO if_wd_context_node ,
        tabname_node TYPE REF TO if_wd_context_node ,
        rootnode_info TYPE REF TO if_wd_context_node_info ,
        child_node_info TYPE REF TO if_wd_context_node_info ,
        stru_tab TYPE REF TO data,
        tablename TYPE string.
  DATA l_description TYPE string.
  DATA l_o_basket_type TYPE REF TO cl_abap_structdescr.
  DATA l_o_layout TYPE REF TO cl_wd_layout_data.
  DATA lt_value_set TYPE zrc_basket_value_set_t.
  FIELD-SYMBOLS <l_value_set> TYPE zrc_basket_value_set.
  FIELD-SYMBOLS: <tab> TYPE table.
  DATA l_t_columns TYPE cl_wd_table_column=>tt_table_column.
  DATA l_path TYPE string.
  DATA cell_edi TYPE REF TO cl_wd_uielement.
  FIELD-SYMBOLS <l_column> TYPE REF TO cl_wd_table_column.
* Get the columns that need to be shown
  l_t_columns = wd_comp_controller->o_basket->get_columns( ).
* Get the context path referenced in the columns
  LOOP AT l_t_columns ASSIGNING <l_column>.
    cell_edi ?= <l_column>->get_table_cell_editor( ).
    l_path = cell_edi->bound__primary_property( ).
    EXIT.
  ENDLOOP.
  SPLIT l_path AT '.' INTO tablename l_path.
  IF i_first_time = abap_true.
* Get description of the basket
    l_description = wd_comp_controller->o_basket->get_description( ).
* Get the basket type
    l_o_basket_type = wd_comp_controller->o_basket->get_basket_type( ).
***** to get root node info in the context.
    rootnode_info = wd_context->get_node_info( ).
* Create child node referencing the table structure
    CALL METHOD rootnode_info->add_new_child_node
      EXPORTING
*    SUPPLY_METHOD                =
*    SUPPLY_OBJECT                =
*    DISPOSE_METHODS              =
*    DISPOSE_OBJECT               =
*    STATIC_ELEMENT_TYPE          =
        name                         = tablename
*    IS_MANDATORY                 = ABAP_FALSE
*    IS_MANDATORY_SELECTION       = ABAP_FALSE
*    IS_MULTIPLE                  = ABAP_TRUE
*    IS_MULTIPLE_SELECTION        = ABAP_TRUE
*    IS_SINGLETON                 = ABAP_FALSE
*    IS_INITIALIZE_LEAD_SELECTION = ABAP_TRUE
        static_element_rtti          = l_o_basket_type
*    IS_STATIC                    = ABAP_TRUE
*    ATTRIBUTES                   =
      RECEIVING
        child_node_info              = child_node_info
        .
* Add value set if there are any
    lt_value_set = wd_comp_controller->o_basket->get_value_set( ).
    IF lt_value_set IS NOT INITIAL.
      LOOP AT lt_value_set ASSIGNING <l_value_set>.
        child_node_info->set_attribute_value_set( name      = <l_value_set>-attribute
                                                  value_set = <l_value_set>-value_set ).
      ENDLOOP.
    ENDIF.
**** to get the ref to rootuielementcontainer in the view
    CALL METHOD i_view->get_root_element
      RECEIVING
        root_view_element = rootelem.
    rootnode ?= rootelem .
**** if group already exists in the, to remove it.
**** this case only occurs, IF you ARE running APPLICATION just by refreshing.
**** precautionary check for existence of group_2
    rootnode->remove_child( id = 'GROUP' ).
**** to create a new group
    group = cl_wd_group=>new_group( view                = i_view
                                    id                  = `GROUP`
                                    design              = `01`
                                    enabled             = abap_true
                                    has_content_padding = abap_true
                                    scrolling_mode      = `02`
                                    visible             = `02` ).
**** to add a caption to the group
    capt_gr = cl_wd_caption=>new_caption( view           = i_view
                                          id             = `CAPT_GR`
                                          enabled        = abap_true
                                          image_first    = abap_true
                                          text           = l_description
                                          text_direction = `02`
                                          visible        = `02` ).
**** To set the Caption to the Group
    group->set_header( capt_gr ).
**** To set the layout for the group_2
    cl_wd_grid_layout=>new_grid_layout( container              = group
                                        cell_padding           = `0`
                                        cell_spacing           = `0`
                                        col_count              = `2`
                                        stretched_horizontally = abap_true
                                        stretched_vertically   = abap_true ).
    cl_wd_flow_data=>new_flow_data( element     = group
                                    cell_design = `04`
                                    v_gutter    = `00` ).
    rootnode->add_child( group ) .
    dyn_node = wd_context->get_child_node( name = tablename ).
*** create a table ui element from context and assigning it to group_2.
    new_tab = cl_wd_dynamic_tool=>create_table_from_node( ui_parent = group
                                                           table_id = 'BASKET'
                                                           node     = dyn_node ).
    new_tab->remove_all_columns( ). " Remove all columns
    new_tab->set_design( 01 ). " Standard
    new_tab->set_read_only( abap_true ).
* Now set only the columns that need to be displayed
    LOOP AT l_t_columns ASSIGNING <l_column>.
      <l_column>->register_to_view( i_view ). " Imp register the columns to this view
      new_tab->add_column( <l_column> ).
    ENDLOOP.
    CALL METHOD new_tab->set_footer_visible
      EXPORTING
        value = abap_false.
    CALL METHOD new_tab->set_selection_mode
      EXPORTING
        value = 06.
  ENDIF.
  IF dyn_node IS INITIAL.
    dyn_node = wd_context->get_child_node( name = tablename ).
  ENDIF.
* Get basket values
  stru_tab = wd_comp_controller->o_basket->get_basket( ).
  ASSIGN stru_tab->* TO <tab>.
  dyn_node->bind_table( <tab> ).
ENDMETHOD.

The highlights in the method are:

  • Getting the name for the new child node from the UI column information, the name has to be same as the basket context so that the new columns will refer this new context automatically.
  • Adding a new child node using the RTTI information of the basket table
  • Adding a new UI table from the above node
  • Adding columns to this table taken from the UI of the original basket
  • Binding of the data to the node

Let’s now see how the call has to be made from the a basket based application. First we have to include the component in the calling Web Dynpro used component.

image

In the calling Web Dynpro component we will make another view called V_PRINT_BASKET.

image

This will have a ViewContainer for embedding the print view. It will also have an outbound plug which will take the navigation back to the calling basket. The window of this application will have the following layout:

image

In the basket view we have to create one inbound plug FROM_V_PRINT_BASKET and one outbound plug TO_V_PRINT_BASKET with a parameter basket of type ZCL_BASKET.

As seen above we have a print button on the basket table. Let’s see the code that will be required to handle the action on the button.

METHOD onactionprint_basket .
  DATA l_o_basket TYPE REF TO zcl_rc_basket.
  DATA l_o_uibasket TYPE REF TO cl_wd_table.
  DATA lt_columns TYPE cl_wd_table_column=>tt_table_column.
  DATA lt_value_set TYPE wdy_key_value_list.
  DATA ls_basket_value_set TYPE zrc_basket_value_set.
  DATA lt_basket_value_set TYPE zrc_basket_value_set_t.
  DATA:
    node_materialsinbasket              TYPE REF TO if_wd_context_node,
    table_materialsinbasket              TYPE TABLE OF if_v_basket=>element_n_basket.
* navigate from <CONTEXT> to <materialsINBASKET> via lead selection
  node_materialsinbasket = wd_context->get_child_node( name = if_v_basket=>wdctx_n_basket ).
* @TODO handle not set lead selection
  IF ( node_materialsinbasket IS INITIAL ).
    RETURN.
  ENDIF.
* get element via lead selection
  node_materialsinbasket->get_static_attributes_table( IMPORTING table = table_materialsinbasket ).
* get the table uielement
  l_o_uibasket ?= wd_this->o_view->get_element( 'T_BASKET' ).
  lt_columns = l_o_uibasket->get_columns( ).
**********************************************************************
* Add value set for dropdown list
  ls_basket_value_set-value_set = wd_assist->get_reg_st_values( ).
  ls_basket_value_set-attribute = 'COL1'.
  APPEND ls_basket_value_set TO lt_basket_value_set.
  ls_basket_value_set-value_set = wd_assist->get_clm_ty_values( ).
  ls_basket_value_set-attribute = 'COL2'.
  APPEND ls_basket_value_set TO lt_basket_value_set.
**********************************************************************
  CREATE OBJECT l_o_basket EXPORTING i_t_basket    = table_materialsinbasket
                                     i_description = 'Basket'
                                     i_o_uibasket  = l_o_uibasket
                                     i_t_columns   = lt_columns
                                     i_t_value_set = lt_basket_value_set.
  wd_this->fire_to_v_print_basket_plg(
    i_o_basket = l_o_basket                       " Ref to zcl_Rc_Basket
  ).
ENDMETHOD.

In the above code:

  1. Get the UI columns
  2. For the columns where drop down by key is used get the value set
  3. Create object basket referring to ZCL_BASKET
  4. Fire outbound plug V_PRINT_BASKET

So these were the necessary steps to do the print basket view from any given table based application.