Samples / Learn / Grid Table

Grid Table

The full sap.ui.table example: a DynamicPage with search, sort, filter, currency columns and row actions - the closest thing here to a finished ALV.

Class
z2ui5_cl_smp_app_070
Repository
abap2UI5/samples
UI5 libraries
sap.f, sap.m, sap.ui.core, sap.ui.table, sap.ui.unified
Lowest UI5 release
1.71
In the browser
Runs in the playground
Controls it builds
sap.f.DynamicPage, sap.f.DynamicPageHeader, sap.f.DynamicPageTitle, sap.m.Button, sap.m.CheckBox, sap.m.FlexBox, sap.m.HBox, sap.m.Input, sap.m.MessageStrip, sap.m.OverflowToolbar, sap.m.Page, sap.m.ProgressIndicator, sap.m.SearchField, sap.m.Shell, sap.m.Text, sap.m.Title, sap.m.VBox, sap.ui.core.mvc.View, sap.ui.table.Column, sap.ui.table.RowAction, sap.ui.table.RowActionItem, sap.ui.table.Table, sap.ui.unified.Currency
Documentation
cookbook/model/tables ↗, tutorials/step-9 ↗, tutorials/step-10 ↗

The class

" @keywords grid alv dynamicpage column row action currency search sort filter
" @summary The full sap.ui.table example: a DynamicPage with search, sort, filter, currency columns and row actions - the closest thing here to a finished ALV.
" @docs https://abap2ui5.github.io/docs/cookbook/model/tables https://abap2ui5.github.io/docs/tutorials/step-9 https://abap2ui5.github.io/docs/tutorials/step-10
CLASS z2ui5_cl_smp_app_070 DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES z2ui5_if_app.

    TYPES:
      BEGIN OF ty_s_tab,
        selkz            TYPE abap_bool,
        row_id           TYPE string,
        product          TYPE string,
        create_date      TYPE string,
        create_by        TYPE string,
        storage_location TYPE string,
        quantity         TYPE i,
        meins            TYPE meins,
        price            TYPE p LENGTH 10 DECIMALS 2,
        waers            TYPE waers,
        selected         TYPE abap_bool,
        process          TYPE string,
        process_state    TYPE string,
      END OF ty_s_tab.

    DATA mv_search_value TYPE string.
    DATA mt_table TYPE STANDARD TABLE OF ty_s_tab WITH EMPTY KEY.
    DATA lv_selkz TYPE abap_bool.

  PROTECTED SECTION.
    DATA client TYPE REF TO z2ui5_if_client.

    METHODS on_event.
    METHODS view_display.
    METHODS set_search.
    METHODS set_data.
    METHODS set_sort.
    METHODS set_filter.

    METHODS set_selkz
      IMPORTING
        iv_selkz TYPE abap_bool.

  PRIVATE SECTION.
ENDCLASS.


CLASS z2ui5_cl_smp_app_070 IMPLEMENTATION.

  METHOD z2ui5_if_app~main.

    me->client     = client.
    IF client->check_on_init( ).

      set_data( ).
      view_display( ).

    ELSEIF client->check_on_navigated( ).
      view_display( ).
    ELSEIF client->check_on_event( ).
      on_event( ).
    ENDIF.

  ENDMETHOD.


  METHOD on_event.

    CASE client->get_event( ).
      WHEN `BUTTON_SEARCH` OR `BUTTON_START`.
        client->message_toast_display( `Search Entries` ).
        set_data( ).
        set_search( ).
      WHEN `SORT`.
        set_sort( ).
      WHEN `FILTER`.
        set_filter( ).
      WHEN `SELKZ`.
        client->message_toast_display( |'Event SELKZ' { lv_selkz } | ).
        set_selkz( lv_selkz ).
      WHEN `ROW_ACTION_ITEM_NAVIGATION`.
        client->message_toast_display( |Event ROW_ACTION_ITEM_NAVIGATION Row ID { client->get_event_arg( ) }| ).
      WHEN `ROW_ACTION_ITEM_EDIT`.
        client->message_toast_display( |Event ROW_ACTION_ITEM_EDIT Row ID { client->get_event_arg( ) }| ).
    ENDCASE.

  ENDMETHOD.


  METHOD set_selkz.

    LOOP AT mt_table REFERENCE INTO DATA(lr_row).
      lr_row->selkz = iv_selkz.
    ENDLOOP.

  ENDMETHOD.


  METHOD set_sort.

    " the sort event carries the column's sortProperty and the requested
    " order - the client sorts its binding too, so both agree, and the
    " backend order is the one the next model push would restore
    DATA(property)   = client->get_event_arg( ).
    DATA(sort_order) = client->get_event_arg( 2 ).

    IF sort_order = `Descending`.
      SORT mt_table BY (property) DESCENDING.
    ELSE.
      SORT mt_table BY (property) ASCENDING.
    ENDIF.
    client->message_toast_display( |Event SORT { property } { sort_order }| ).

  ENDMETHOD.


  METHOD set_filter.

    " the filter event carries the column's filterProperty and the typed
    " value: a contains-filter over that one component, on the full data
    DATA(property) = client->get_event_arg( ).
    DATA(value)    = to_upper( client->get_event_arg( 2 ) ).

    set_data( ).
    set_search( ).

    IF value IS INITIAL.
      client->message_toast_display( |Event FILTER { property } cleared| ).
      RETURN.
    ENDIF.

    DATA(lt_all) = mt_table.
    mt_table = VALUE #( ).

    LOOP AT lt_all REFERENCE INTO DATA(lr_row).
      ASSIGN COMPONENT property OF STRUCTURE lr_row->* TO FIELD-SYMBOL(<field>).

      IF sy-subrc <> 0.
        CONTINUE.
      ENDIF.

      IF to_upper( |{ <field> }| ) CS value.
        INSERT lr_row->* INTO TABLE mt_table.
      ENDIF.
    ENDLOOP.
    client->message_toast_display( |Event FILTER { property } { value }| ).

  ENDMETHOD.


  METHOD view_display.

    DATA(view) = z2ui5_cl_ui5_view_builder=>factory(
        )->ele( n = `View` ns = `mvc`
            )->a( n = `displayBlock` v = `true`
            )->a( n = `height`       v = `100%`
            )->a( n = `xmlns`        v = `sap.m`
            )->a( n = `xmlns:mvc`    v = `sap.ui.core.mvc`
            )->a( n = `xmlns:f`      v = `sap.f`
            )->a( n = `xmlns:table`  v = `sap.ui.table`
            )->a( n = `xmlns:u`      v = `sap.ui.unified` ).

    DATA(page1) = view->ele( `Shell`
        )->ele( `Page`
            )->a( n = `title`          v = `abap2UI5 - Grid Table - Full Example with sap.ui.table`
            )->a( n = `showNavButton`  b = client->check_app_prev_stack( )
            )->a( n = `navButtonPress` v = client->_event_nav_app_leave( )
            )->a( n = `class`          v = `sapUiContentPadding`
            )->a( n = `id`             v = `page_main` ).

    page1->tag( `MessageStrip`
        )->a( n = `text`     v = `A full sap.ui.table.Table inside a DynamicPage: fixed column, row-action buttons, ` &&
                   `progress-indicator and currency cells, plus search, sort and filter events that are answered ` &&
                   `in the backend: the search rebuilds the rows, the column sort and the column filter sort and ` &&
                   `filter the internal table with the property the event carries.`
        )->a( n = `type`     v = `Information`
        )->a( n = `showIcon` b = abap_true
        )->a( n = `class`    v = `sapUiSmallMargin` ).

    DATA(page) = page1->ele( n = `DynamicPage` ns = `f`
        )->a( n = `headerExpanded` b = abap_true ).

    DATA(header_title) = page->ele( n = `title` ns = `f`
        )->ele( n = `DynamicPageTitle` ns = `f` ).
    header_title->ele( n = `heading` ns = `f`
        )->ele( `HBox`
            )->tag( `Title`
                )->a( n = `text` v = `Search Field` ).
    header_title->ele( n = `expandedContent` ns = `f` ).
    header_title->ele( n = `snappedContent` ns = `f` ).

    " ns = `f` like every other DynamicPage aggregation here: without it the
    " tag renders as <header xmlns="sap.m"/>, and UI5 looks for a DEFAULT
    " aggregation on sap.f.DynamicPage - which has none - so the view dies with
    " "Cannot add direct child without default aggregation defined"
    DATA(lo_box) = page->ele( n = `header` ns = `f`
        )->ele( n = `DynamicPageHeader` ns = `f`
            )->a( n = `pinnable` b = abap_true
            )->ele( `FlexBox`
                )->a( n = `alignItems`     v = `Start`
                )->a( n = `justifyContent` v = `SpaceBetween`
                )->ele( `FlexBox`
                    )->a( n = `alignItems` v = `Start` ).

    lo_box->ele( `VBox`
        )->tag( `Text`
            )->a( n = `text` v = `Search`
        )->tag( `SearchField`
            )->a( n = `width`       v = `17.5rem`
            )->a( n = `search`      v = client->_event( `BUTTON_SEARCH` )
            )->a( n = `value`       v = client->_bind( mv_search_value )
            )->a( n = `id`          v = `SEARCH`
            )->a( n = `placeholder` v = `Search products` ).

    lo_box->end(
        )->ele( `HBox`
            )->a( n = `justifyContent` v = `End`
            )->tag( `Button`
                )->a( n = `press` v = client->_event( `BUTTON_START` )
                )->a( n = `text`  v = `Go`
                )->a( n = `type`  v = `Emphasized` ).

    DATA(cont) = page->ele( n = `content` ns = `f` ).

    DATA(tab) = cont->ele( n = `Table` ns = `table`
        )->a( n = `rows`               v = client->_bind( mt_table )
        )->a( n = `alternateRowColors` b = abap_true
        )->a( n = `fixedColumnCount`   v = `1`
        )->a( n = `rowActionCount`     v = `2`
        )->a( n = `selectionMode`      v = `None`
        )->a( n = `filter`             v = client->_event( val   = `FILTER`
                                                            t_arg = VALUE #( ( `${$parameters>/column}.getFilterProperty()` )
                                                                             ( `${$parameters>/value}` ) ) )
        )->a( n = `sort`               v = client->_event( val = `SORT`
                                                            t_arg = VALUE #( ( `${$parameters>/column}.getSortProperty()` )
                                                                             ( `${$parameters>/sortOrder}` ) ) ) ).
    tab->ele( n = `extension` ns = `table`
        )->ele( `OverflowToolbar`
            )->tag( `Title`
                )->a( n = `text` v = `Products` ).
    DATA(lo_columns) = tab->ele( n = `columns` ns = `table` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width` v = `4rem`
        )->tag( `CheckBox`
            )->a( n = `selected` v = client->_bind( lv_selkz )
            )->a( n = `enabled`  b = abap_true
            )->a( n = `select`   v = client->_event( `SELKZ` )
        )->ele( n = `template` ns = `table`
            )->tag( `CheckBox`
                )->a( n = `selected` v = `{SELKZ}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `5rem`
        )->a( n = `sortProperty`   v = `ROW_ID`
        )->a( n = `filterProperty` v = `ROW_ID`
        )->tag( `Text`
            )->a( n = `text` v = `ID`
        )->ele( n = `template` ns = `table`
            )->tag( `Text`
                )->a( n = `text` v = `{ROW_ID}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `11rem`
        )->a( n = `sortProperty`   v = `PROCESS`
        )->a( n = `filterProperty` v = `PROCESS`
        )->tag( `Text`
            )->a( n = `text` v = `Process Indicator`
        )->ele( n = `template` ns = `table`
            )->tag( `ProgressIndicator`
                )->a( n = `class`        v = `sapUiSmallMarginBottom`
                )->a( n = `percentValue` v = `{PROCESS}`
                )->a( n = `displayValue` v = `{PROCESS} %`
                )->a( n = `showValue`    v = `true`
                )->a( n = `state`        v = `{PROCESS_STATE}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `11rem`
        )->a( n = `sortProperty`   v = `PRODUCT`
        )->a( n = `filterProperty` v = `PRODUCT`
        )->tag( `Text`
            )->a( n = `text` v = `Product`
        )->ele( n = `template` ns = `table`
            )->tag( `Input`
                )->a( n = `editable` b = abap_false
                )->a( n = `value`    v = `{PRODUCT}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `11rem`
        )->a( n = `sortProperty`   v = `CREATE_DATE`
        )->a( n = `filterProperty` v = `CREATE_DATE`
        )->tag( `Text`
            )->a( n = `text` v = `Date`
        )->ele( n = `template` ns = `table`
            )->tag( `Text`
                )->a( n = `text` v = `{CREATE_DATE}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `11rem`
        )->a( n = `sortProperty`   v = `CREATE_BY`
        )->a( n = `filterProperty` v = `CREATE_BY`
        )->tag( `Text`
            )->a( n = `text` v = `Name`
        )->ele( n = `template` ns = `table`
            )->tag( `Text`
                )->a( n = `text` v = `{CREATE_BY}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `11rem`
        )->a( n = `sortProperty`   v = `STORAGE_LOCATION`
        )->a( n = `filterProperty` v = `STORAGE_LOCATION`
        )->tag( `Text`
            )->a( n = `text` v = `Location`
        )->ele( n = `template` ns = `table`
            )->tag( `Text`
                )->a( n = `text` v = `{STORAGE_LOCATION}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `11rem`
        )->a( n = `sortProperty`   v = `QUANTITY`
        )->a( n = `filterProperty` v = `QUANTITY`
        )->tag( `Text`
            )->a( n = `text` v = `Quantity`
        )->ele( n = `template` ns = `table`
            )->tag( `Text`
                )->a( n = `text` v = `{QUANTITY}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `6rem`
        )->a( n = `sortProperty`   v = `MEINS`
        )->a( n = `filterProperty` v = `MEINS`
        )->tag( `Text`
            )->a( n = `text` v = `Unit`
        )->ele( n = `template` ns = `table`
            )->tag( `Text`
                )->a( n = `text` v = `{MEINS}` ).
    lo_columns->ele( n = `Column` ns = `table`
        )->a( n = `width`          v = `11rem`
        )->a( n = `sortProperty`   v = `PRICE`
        )->a( n = `filterProperty` v = `PRICE`
        )->tag( `Text`
            )->a( n = `text` v = `Price`
        )->ele( n = `template` ns = `table`
            )->ele( n = `Currency` ns = `u`
                )->a( n = `value`    v = `{PRICE}`
                )->a( n = `currency` v = `{WAERS}` ).
    lo_columns->end(
        )->ele( n = `rowActionTemplate` ns = `table`
            )->ele( n = `RowAction` ns = `table`
                )->ele( n = `RowActionItem` ns = `table`
                    )->a( n = `type`  v = `Navigation`
                    )->a( n = `press` v = client->_event( val = `ROW_ACTION_ITEM_NAVIGATION` arg = `${ROW_ID}` )
                )->end(
                )->ele( n = `RowActionItem` ns = `table`
                    )->a( n = `icon`  v = `sap-icon://edit`
                    )->a( n = `text`  v = `Edit`
                    )->a( n = `press` v = client->_event( val = `ROW_ACTION_ITEM_EDIT` arg = `${ROW_ID}` ) ).

    client->view_display( view->stringify( ) ).

  ENDMETHOD.


  METHOD set_data.

    mt_table = VALUE #(
        ( selkz = abap_false row_id = `1` product = `table`    create_date = `2023-01-01` create_by = `Olaf`   storage_location = `AREA_001` quantity = 400 meins = `ST` price = `1000.50` waers = `EUR` process = `10` process_state = `None` )
        ( selkz = abap_false row_id = `2` product = `chair`    create_date = `2022-01-01` create_by = `Karlo`  storage_location = `AREA_001` quantity = 123 meins = `ST` price = `2000.55` waers = `USD` process = `20` process_state = `Warning` )
        ( selkz = abap_false row_id = `3` product = `sofa`     create_date = `2021-05-01` create_by = `Elin`   storage_location = `AREA_002` quantity = 700 meins = `ST` price = `3000.11` waers = `CNY` process = `30` process_state = `Success` )
        ( selkz = abap_false row_id = `4` product = `computer` create_date = `2023-01-27` create_by = `Theo`   storage_location = `AREA_002` quantity = 200 meins = `ST` price = `4000.88` waers = `USD` process = `40` process_state = `Information` )
        ( selkz = abap_false row_id = `5` product = `printer`  create_date = `2023-01-01` create_by = `Renate` storage_location = `AREA_003` quantity = 90  meins = `ST` price = `5000.47` waers = `EUR` process = `70` process_state = `Warning` )
        ( selkz = abap_false row_id = `6` product = `table2`   create_date = `2023-01-01` create_by = `Angela` storage_location = `AREA_003` quantity = 110 meins = `ST` price = `6000.33` waers = `GBP` process = `90` process_state = `Error` ) ).

  ENDMETHOD.


  METHOD set_search.

    IF mv_search_value IS NOT INITIAL.

      " uppercase against uppercase - a case-insensitive search, as in the
      " twins z2ui5_cl_smp_app_053 and z2ui5_cl_smp_app_059
      DATA(lv_search) = to_upper( mv_search_value ).

      " Collected rather than deleted in place: a DELETE ... INDEX sy-tabix
      " inside a LOOP over the same table shifts the rows under the loop's own
      " cursor and silently SKIPS the row after each deletion, so the search
      " would return wrong rows. Found 2026-08-17.
      DATA(lt_all) = mt_table.
      mt_table = VALUE #( ).

      LOOP AT lt_all REFERENCE INTO DATA(lr_row).
        DATA(lv_row) = ``.
        DATA(lv_index) = 1.
        DO.
          ASSIGN COMPONENT lv_index OF STRUCTURE lr_row->* TO FIELD-SYMBOL(<field>).

          IF sy-subrc <> 0.
            EXIT.
          ENDIF.
          lv_row   = lv_row && <field>.
          lv_index = lv_index + 1.
        ENDDO.

        IF to_upper( lv_row ) CS lv_search.
          APPEND lr_row->* TO mt_table.
        ENDIF.
      ENDLOOP.
    ENDIF.

  ENDMETHOD.

ENDCLASS.