abap2UI5

Grid Table

Spreadsheet-style copy and paste on a grid table: select a cell block and copy it, or paste rows from Excel into the table and let the backend append them - needs UI5 1.119 or newer.

Z2UI5_CL_SMP_APP_538

▶ Run it here

Learn Grid Table UI5 1.119 frontend action

The facts

Repository
abap2UI5/samples
Class
Z2UI5_CL_SMP_APP_538
Source file
z2ui5_cl_smp_app_538.clas.abap ↗
Category
Grid Table
Learning path
Show many rows
Minimum UI5
1.119
In the playground
runs in the browser, with no system and nothing installed
Marked
(A) — Performs a frontend action: client->follow_up_action( ), or drag and drop

Keywords: clipboard, copy, paste, cellselector, copyprovider, pasteprovider, excel, spreadsheet, grid, control_by_id

Controls it builds

Read out of the builder chain by the abap2UI5 linter, not from the category this sample is filed under — which is what makes “which samples build one of these” a question the catalogue can answer at all.

Libraries

Run it here

The sample running, in this page — the app on its own, with no editor over it. Switch to Playground with this code

Nothing is installed and nothing is sent anywhere: the ABAP is compiled to JavaScript in this browser and runs against abap2UI5 itself.

The ABAP

z2ui5_cl_smp_app_538.clas.abap — 274 lines Read it on GitHub ↗
" @keywords clipboard copy paste cellselector copyprovider pasteprovider excel spreadsheet grid control_by_id
" @summary Spreadsheet-style copy and paste on a grid table: select a cell block and copy it, or paste rows from Excel into the table and let the backend append them - needs UI5 1.119 or newer.
CLASS z2ui5_cl_smp_app_538 DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES z2ui5_if_app.

    TYPES:
      BEGIN OF ty_s_product,
        product_id TYPE string,
        name       TYPE string,
        category   TYPE string,
        price      TYPE string,
        currency   TYPE string,
      END OF ty_s_product.
    DATA t_products TYPE STANDARD TABLE OF ty_s_product WITH EMPTY KEY.

  PROTECTED SECTION.
    TYPES ty_t_rows TYPE STANDARD TABLE OF string_table WITH EMPTY KEY.

    DATA client TYPE REF TO z2ui5_if_client.

    METHODS on_init.
    METHODS on_event.
    METHODS on_event_paste.
    METHODS view_display.

    METHODS pasted_rows
      IMPORTING
        json          TYPE string
      RETURNING
        VALUE(result) TYPE ty_t_rows.

  PRIVATE SECTION.
ENDCLASS.


CLASS z2ui5_cl_smp_app_538 IMPLEMENTATION.

  METHOD z2ui5_if_app~main.

    me->client = client.
    IF client->check_on_init( ).
      on_init( ).
    ELSEIF client->check_on_navigated( ).
      view_display( ).
    ELSEIF client->check_on_event( ).
      on_event( ).
    ENDIF.

  ENDMETHOD.


  METHOD on_init.

    t_products = VALUE #(
        ( product_id = `HT-1000` name = `Notebook Basic 15`        category = `Laptops`      price = `956.00`  currency = `EUR` )
        ( product_id = `HT-1001` name = `Notebook Basic 17`        category = `Laptops`      price = `1249.00` currency = `EUR` )
        ( product_id = `HT-1010` name = `Notebook Professional 15` category = `Laptops`      price = `1999.00` currency = `EUR` )
        ( product_id = `HT-1030` name = `Ergo Screen E-I`          category = `Flat Screens` price = `230.00`  currency = `EUR` )
        ( product_id = `HT-1040` name = `Laser Professional Eco`   category = `Printers`     price = `830.00`  currency = `EUR` )
        ( product_id = `HT-1063` name = `Ergonomic Keyboard`       category = `Keyboards`    price = `14.00`   currency = `EUR` )
        ( product_id = `HT-1070` name = `Photo Scan`               category = `Scanners`     price = `129.00`  currency = `EUR` ) ).

    view_display( ).

  ENDMETHOD.


  METHOD on_event.

    CASE client->get_event( ).
      WHEN `COPY`.
        " the CopyProvider's copy event - fired before the clipboard is written
        client->message_toast_display( `Selection copied to the clipboard` ).
      WHEN `PASTE`.
        on_event_paste( ).
    ENDCASE.

  ENDMETHOD.


  METHOD on_event_paste.

    " the pasted rows arrive in the backend, which decides what they mean -
    " here: the cells, in column order, become new products
    DATA(rows) = pasted_rows( client->get_event_arg( ) ).

    LOOP AT rows INTO DATA(cells).
      INSERT VALUE #( product_id = VALUE #( cells[ 1 ] OPTIONAL )
                      name       = VALUE #( cells[ 2 ] OPTIONAL )
                      category   = VALUE #( cells[ 3 ] OPTIONAL )
                      price      = VALUE #( cells[ 4 ] OPTIONAL )
                      currency   = VALUE #( cells[ 5 ] OPTIONAL ) ) INTO TABLE t_products.
    ENDLOOP.

    client->message_toast_display( |{ lines( rows ) } row(s) pasted and appended| ).

  ENDMETHOD.


  METHOD pasted_rows.

    " the paste event's data parameter is string[][], so it reaches the
    " backend as JSON: [["HT-2000","Tablet"],["HT-2001","Phone"]]. One pass
    " over it - an opening quote starts a cell, a closing one ends it, and a
    " bracket that closes the second level ends a row.
    DATA row       TYPE string_table.
    DATA cell      TYPE string.
    DATA depth     TYPE i.
    DATA in_string TYPE abap_bool.
    DATA escaped   TYPE abap_bool.

    DO strlen( json ) TIMES.
      DATA(char) = substring( val = json
                              off = sy-index - 1
                              len = 1 ).

      IF in_string = abap_true.

        IF escaped = abap_true.
          cell    = |{ cell }{ char }|.
          escaped = abap_false.
        ELSEIF char = `\`.
          escaped = abap_true.
        ELSEIF char = `"`.
          INSERT cell INTO TABLE row.
          cell      = ``.
          in_string = abap_false.
        ELSE.
          cell = |{ cell }{ char }|.
        ENDIF.

      ELSEIF char = `"`.
        in_string = abap_true.
      ELSEIF char = `[`.
        depth = depth + 1.
      ELSEIF char = `]`.

        IF depth = 2.
          INSERT row INTO TABLE result.
          row = VALUE #( ).
        ENDIF.
        depth = depth - 1.

      ENDIF.
    ENDDO.

  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:core`    v = `sap.ui.core`
            )->a( n = `xmlns:table`   v = `sap.ui.table`
            )->a( n = `xmlns:plugins` v = `sap.m.plugins`
            )->a( n = `xmlns:app`     v = `http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1`
            " the CopyProvider's extractData callback ships with the framework
            )->a( n = `core:require`  v = `{Clipboard: 'z2ui5/model/clipboard'}` ).
    DATA(page) = view->ele( `Shell`
        )->ele( `Page`
            )->a( n = `title`          v = `abap2UI5 - Grid Table - Copy & Paste, CellSelector (UI5 1.119+)`
            )->a( n = `showNavButton`  b = client->check_app_prev_stack( )
            )->a( n = `navButtonPress` v = client->_event_nav_app_leave( ) ).

    page->tag( `MessageStrip`
        )->a( n = `text`     v = `Drag over the cells to select a block, then press Copy or Ctrl+C - the CopyProvider writes ` &&
                   `it to the clipboard, as text for a spreadsheet. Copy a few rows in Excel and press Paste or ` &&
                   `Ctrl+V on the table: the rows travel to the backend, which appends them. Needs UI5 1.119 or newer.`
        )->a( n = `type`     v = `Information`
        )->a( n = `showIcon` b = abap_true
        )->a( n = `class`    v = `sapUiSmallMargin` ).

    DATA(table) = page->ele( n = `Table` ns = `table`
        )->a( n = `id`              v = `products`
        )->a( n = `rows`            v = client->_bind( t_products )
        )->a( n = `visibleRowCount` v = `8`
        )->a( n = `selectionMode`   v = `MultiToggle`
        )->a( n = `class`           v = `sapUiSmallMargin`
        )->a( n = `paste`           v = client->_event( val = `PASTE` arg = `${$parameters>/data}` ) ).

    table->ele( n = `dependents` ns = `table`
        )->tag( n = `CellSelector` ns = `plugins`
        )->tag( n = `CopyProvider` ns = `plugins`
            )->a( n = `id`          v = `copyProvider`
            )->a( n = `extractData` v = `Clipboard.extractData`
            )->a( n = `copy`        v = client->_event( `COPY` ) ).

    table->ele( n = `extension` ns = `table`
        )->ele( `OverflowToolbar`
            )->tag( `Title`
                )->a( n = `text` v = `Products`
            )->tag( `ToolbarSpacer`
            " the copy has to run inside the click - the browser only lets a
            " user gesture write the clipboard - so no roundtrip: a frontend
            " action calls copySelectionData on the plugin directly
            )->tag( `OverflowToolbarButton`
                )->a( n = `icon`    v = `sap-icon://copy`
                )->a( n = `text`    v = `Copy`
                )->a( n = `tooltip` v = `Copy`
                )->a( n = `press`   v = client->follow_up_action( val   = client->cs_event-control_by_id
                                                                  t_arg = VALUE #( ( `copyProvider` ) ( `copySelectionData` ) ( `X` ) ) )

            " the PasteProvider turns this button into a paste button for the table
            )->ele( `Button`
                )->ele( `dependents`
                    )->tag( n = `PasteProvider` ns = `plugins`
                        )->a( n = `pasteFor` v = `products` ).

    " app:bindings names what a column copies - the clipboard module reads
    " it, one clipboard cell per path
    table->ele( n = `columns` ns = `table`
        )->ele( n = `Column` ns = `table`
            )->a( n = `app:bindings` v = `PRODUCT_ID`
            )->tag( `Label`
                )->a( n = `text` v = `Product ID`
            )->ele( n = `template` ns = `table`
                )->tag( `Text`
                    )->a( n = `text` v = `{PRODUCT_ID}`

            )->end(
        )->end(
        )->ele( n = `Column` ns = `table`
            )->a( n = `app:bindings` v = `NAME`
            )->tag( `Label`
                )->a( n = `text` v = `Name`
            )->ele( n = `template` ns = `table`
                )->tag( `Text`
                    )->a( n = `text` v = `{NAME}`

            )->end(
        )->end(
        )->ele( n = `Column` ns = `table`
            )->a( n = `app:bindings` v = `CATEGORY`
            )->tag( `Label`
                )->a( n = `text` v = `Category`
            )->ele( n = `template` ns = `table`
                )->tag( `Text`
                    )->a( n = `text` v = `{CATEGORY}`

            )->end(
        )->end(
        )->ele( n = `Column` ns = `table`
            )->a( n = `app:bindings` v = `PRICE`
            )->tag( `Label`
                )->a( n = `text` v = `Price`
            )->ele( n = `template` ns = `table`
                )->tag( `Text`
                    )->a( n = `text` v = `{PRICE}`

            )->end(
        )->end(
        )->ele( n = `Column` ns = `table`
            )->a( n = `app:bindings` v = `CURRENCY`
            )->tag( `Label`
                )->a( n = `text` v = `Currency`
            )->ele( n = `template` ns = `table`
                )->tag( `Text`
                    )->a( n = `text` v = `{CURRENCY}`

            )->end(
        )->end( ).

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

  ENDMETHOD.

ENDCLASS.

More in Grid Table

Search every abap2UI5 sample · the full list on one page

On this page