abap2UI5

Flexible Column Layout with Routing and first page in fullscreen only

Flexible Column Layout as an app with routing that displays different pages in the initial column. The first page is only displayed in OneColumn layout type

Z2UI5_CL_SMPC_APP_578

Controls sap.f UI5 1.73

The facts

Repository
abap2UI5/samples-controls
Class
Z2UI5_CL_SMPC_APP_578
Source file
z2ui5_cl_smpc_app_578.clas.abap
Library
sap.f
UI5 entity
sap.f.FlexibleColumnLayout
Demo kit sample
sap.f.sample.FlexibleColumnLayoutWithFullscreenPage
SAP's own sample
running at sdk.openui5.org ↗
Minimum UI5
1.73
In the playground
runs in the browser, with no system and nothing installed

Keywords: flexiblecolumnlayout, flexible, column, layout, sap.f, flexiblecolumnlayoutwithfullscreenpage, dynamicpagetitle, title, table, text, columnlistitem, objectidentifier

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_smpc_app_578.clas.abap — 1253 lines Read it on GitHub ↗
" @keywords flexiblecolumnlayout flexible column layout sap.f flexiblecolumnlayoutwithfullscreenpage dynamicpagetitle title table text columnlistitem objectidentifier
" @summary Flexible Column Layout as an app with routing that displays different pages in the initial column. The first page is only displayed in OneColumn layout type
CLASS z2ui5_cl_smpc_app_578 DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES z2ui5_if_app.

    TYPES: BEGIN OF ty_s_product,
             productid     TYPE string,
             name          TYPE string,
             maincategory  TYPE string,
             category      TYPE string,
             suppliername  TYPE string,
             productpicurl TYPE string,
             description   TYPE string,
             price         TYPE p LENGTH 9 DECIMALS 2,
             currencycode  TYPE string,
           END OF ty_s_product.
    TYPES ty_t_product TYPE STANDARD TABLE OF ty_s_product WITH EMPTY KEY.
    TYPES: BEGIN OF ty_s_supplier,
             text TYPE string,
           END OF ty_s_supplier.
    TYPES ty_t_supplier TYPE STANDARD TABLE OF ty_s_supplier WITH EMPTY KEY.

    DATA t_products   TYPE ty_t_product.
    DATA t_rows       TYPE ty_t_product.
    DATA t_suppliers  TYPE ty_t_supplier.
    " /ProductCollectionStats/Filters/0/values - the categories page of the
    " begin column, which this sample starts on
    DATA t_categories TYPE ty_t_supplier.

    " the FlexibleColumnLayout state the router drives in the original
    DATA layout      TYPE string VALUE `OneColumn`.
    DATA total_count TYPE i.
    DATA descending  TYPE abap_bool.

    " the beginColumnPages page the LIVE FlexibleColumnLayout was last sent to.
    " A column position is control state, not model state, so view_display( )
    " loses it (app 585 idiom); this field is what lets it be re-issued
    DATA begin_page  TYPE string.

    " the product the mid column shows and the supplier the end column shows
    DATA d_name          TYPE string.
    DATA d_productid     TYPE string.
    DATA d_maincategory  TYPE string.
    DATA d_category      TYPE string.
    DATA d_suppliername  TYPE string.
    DATA d_productpicurl TYPE string.
    DATA d_description   TYPE string.
    DATA d_price         TYPE string.
    DATA dd_text         TYPE string.

  PROTECTED SECTION.
    DATA client TYPE REF TO z2ui5_if_client.

    " the router state the original keeps (currentRouteName + the route's
    " arguments): the category travels as its NAME (URL-encoded, spaces as
    " %20), product and supplier as INDICES into the mock collections
    DATA route          TYPE string VALUE `list`.
    DATA route_category TYPE string.
    DATA product_ix     TYPE i.
    DATA supplier_ix    TYPE i.

    METHODS view_display.
    METHODS on_event.
    METHODS detail_bind IMPORTING productid TYPE string.
    METHODS category_apply IMPORTING iv_category TYPE string.
    METHODS hash_apply IMPORTING iv_hash TYPE string.
    METHODS hash_push IMPORTING check_replace TYPE abap_bool OPTIONAL.
    METHODS model_init.

  PRIVATE SECTION.
ENDCLASS.


CLASS z2ui5_cl_smpc_app_578 IMPLEMENTATION.

  METHOD z2ui5_if_app~main.

    me->client = client.
    IF client->check_on_init( ).
      model_init( ).
      t_rows = t_products.
      total_count = lines( t_products ).
      view_display( ).
    ELSEIF client->check_on_navigated( ).
      view_display( ).
    ELSEIF client->check_on_event( ).
      on_event( ).
    ENDIF.

  ENDMETHOD.


  METHOD view_display.

    " the router also matches a deep link / reload (`#/detailDetail/Laptops/
    " 0/TwoColumnsMidExpanded`): the live hash rides in s_config-hash on
    " every request; applying it is idempotent, so a rebuild whose hash
    " matches the state simply re-derives it
    DATA(lv_hash) = client->get( )-s_config-hash.
    IF lv_hash IS NOT INITIAL AND lv_hash <> `#`.
      hash_apply( lv_hash ).
    ENDIF.

    DATA(view) = z2ui5_cl_ui5_view_builder=>factory( ).

    DATA(fcl) = view->ele( n = `View` ns = `mvc`
        )->a( n = `height`      v = `100%`
        )->a( n = `xmlns`       v = `sap.m`
        )->a( n = `xmlns:f`     v = `sap.f`
        )->a( n = `xmlns:form`  v = `sap.ui.layout.form`
        )->a( n = `xmlns:mvc`   v = `sap.ui.core.mvc`
        )->a( n = `xmlns:uxap`  v = `sap.uxap`

        )->ele( n = `FlexibleColumnLayout` ns = `f`
            )->a( n = `id`               v = `fcl`
            )->a( n = `backgroundDesign` v = `Translucent`
            " the original wires stateChange to onStateChanged: only a layout
            " change by a NAVIGATION ARROW replace-navTo's the URL - the flag
            " and the new layout travel with the event, the backend guards on it
            )->a( n = `stateChange`      v = client->_event( val = `STATE_CHANGED` t_arg = VALUE #( ( `${$parameters>/isNavigationArrow}` ) ( `${$parameters>/layout}` ) ) )
            )->a( n = `layout`           v = client->_bind( layout ) ).

    " List.view.xml - the categories page the sample starts on, and
    " Detail.view.xml - the products page that replaces it in the begin column
    DATA(begin_column) = fcl->ele( n = `beginColumnPages` ns = `f` ).

    begin_column->ele( n = `DynamicPage` ns = `f`
        )->a( n = `id`                       v = `categoriesPage`
        )->a( n = `toggleHeaderOnTitleClick` v = `false`

        )->ele( n = `title` ns = `f`
            )->ele( n = `DynamicPageTitle` ns = `f`
                )->ele( n = `heading` ns = `f`
                    )->tag( `Title`
                        )->a( n = `text` v = `Categories`

                )->end(
            )->end(
        )->end(
        )->ele( n = `content` ns = `f`
            )->ele( `Table`
                )->a( n = `id`    v = `categoriesTable`
                )->a( n = `mode`  v = `SingleSelectMaster`
                )->a( n = `inset` v = `false`
                )->a( n = `class` v = `sapFDynamicPageAlignContent`
                )->a( n = `width` v = `auto`
                )->a( n = `items` v = client->_bind( t_categories )
                )->a( n = `itemPress` v = client->_event( val = `CATEGORY_ITEM` arg = `${$parameters>/listItem}.getBindingContext().getProperty('TEXT')` )

                )->ele( `columns`
                    )->ele( `Column`
                        )->a( n = `width` v = `12em`

                        )->tag( `Text`
                            )->a( n = `text` v = `Category`

                    )->end(
                )->end(
                )->ele( `items`
                    )->ele( `ColumnListItem`
                        )->a( n = `type` v = `Navigation`

                        )->ele( `cells`
                            )->tag( `ObjectIdentifier`
                                )->a( n = `title` v = `{TEXT}`

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

    begin_column->ele( n = `DynamicPage` ns = `f`
        )->a( n = `id`                       v = `dynamicPageId`
        )->a( n = `toggleHeaderOnTitleClick` v = `false`

        )->ele( n = `title` ns = `f`
            )->ele( n = `DynamicPageTitle` ns = `f`
                )->ele( n = `heading` ns = `f`
                    )->tag( `Title`
                        )->a( n = `text` v = `Products`

                )->end(
            )->end(
        )->end(
        )->ele( n = `content` ns = `f`
            )->ele( `Table`
                )->a( n = `id`    v = `productsTable`
                )->a( n = `mode`  v = `SingleSelectMaster`
                )->a( n = `inset` v = `false`
                )->a( n = `class` v = `sapFDynamicPageAlignContent`
                )->a( n = `width` v = `auto`
                )->a( n = `items` v = client->_bind( t_rows )
                )->a( n = `itemPress` v = client->_event( val = `LIST_ITEM` arg = `${$parameters>/listItem}.getBindingContext().getProperty('PRODUCTID')` )

                )->ele( `headerToolbar`
                    )->ele( `OverflowToolbar`
                        )->tag( `ToolbarSpacer`
                        )->tag( `SearchField`
                            )->a( n = `width`  v = `17.5rem`
                            )->a( n = `search` v = client->_event( val = `SEARCH` arg = `${$parameters>/query}` )
                        )->tag( `OverflowToolbarButton`
                            )->a( n = `icon`    v = `sap-icon://add`
                            )->a( n = `type`    v = `Transparent`
                            )->a( n = `tooltip` v = `Add`
                            )->a( n = `press`   v = client->_event( `ADD` )
                        )->tag( `OverflowToolbarButton`
                            )->a( n = `icon`    v = `sap-icon://sort`
                            )->a( n = `type`    v = `Transparent`
                            )->a( n = `tooltip` v = `Sort`
                            )->a( n = `press`   v = client->_event( `SORT` )

                    )->end(
                )->end(
                )->ele( `columns`
                    )->ele( `Column`
                        )->a( n = `width` v = `12em`

                        )->tag( `Text`
                            )->a( n = `text` v = `Product`

                    )->end(
                    )->ele( `Column`
                        )->a( n = `hAlign` v = `End`

                        )->tag( `Text`
                            )->a( n = `text` v = `Price`

                    )->end(
                )->end(
                )->ele( `items`
                    )->ele( `ColumnListItem`
                        )->a( n = `type` v = `Navigation`

                        )->ele( `cells`
                            )->tag( `ObjectIdentifier`
                                )->a( n = `title` v = `{NAME}`
                                )->a( n = `text`  v = `{PRODUCTID}`
                            )->tag( `ObjectNumber`
                                )->a( n = `number` v = |\{ parts:[\{path:'PRICE'\},\{path:'CURRENCYCODE'\}], type:'sap.ui.model.type.Currency', formatOptions:\{showMeasure:false\} \}|
                                )->a( n = `unit`   v = `{CURRENCYCODE}`

                        )->end(
                    )->end(
                )->end(
            )->end(
        )->end(
        )->ele( n = `footer` ns = `f`
            )->ele( `OverflowToolbar`
                )->tag( `ToolbarSpacer`
                )->tag( `Button`
                    )->a( n = `type` v = `Accept`
                    )->a( n = `text` v = `Accept`
                )->tag( `Button`
                    )->a( n = `type` v = `Reject`
                    )->a( n = `text` v = `Reject`

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

    " Detail.view.xml - the ObjectPage of the mid column
    DATA(detail) = fcl->ele( n = `midColumnPages` ns = `f`
        )->ele( n = `ObjectPageLayout` ns = `uxap`
            )->a( n = `id`                          v = `ObjectPageLayout`
            )->a( n = `showTitleInHeaderContent`    v = `true`
            )->a( n = `alwaysShowContentHeader`     v = `false`
            )->a( n = `preserveHeaderStateOnScroll` v = `false`
            )->a( n = `headerContentPinnable`       v = `true`
            )->a( n = `isChildPage`                 v = `true`
            )->a( n = `upperCaseAnchorBar`          v = `false` ).

    DATA(detail_title) = detail->ele( n = `headerTitle` ns = `uxap`
        )->ele( n = `ObjectPageDynamicHeaderTitle` ns = `uxap` ).

    detail_title->ele( n = `expandedHeading` ns = `uxap`
        )->tag( `Title`
            )->a( n = `text`     v = client->_bind( d_name )
            )->a( n = `wrapping` v = `true`
            )->a( n = `class`    v = `sapUiSmallMarginEnd`

    )->end(
        )->ele( n = `snappedHeading` ns = `uxap`
            )->ele( `FlexBox`
                )->a( n = `wrap`         v = `Wrap`
                )->a( n = `fitContainer` v = `true`
                )->a( n = `alignItems`   v = `Center`

                )->ele( `FlexBox`
                    )->a( n = `wrap`         v = `NoWrap`
                    )->a( n = `fitContainer` v = `true`
                    )->a( n = `alignItems`   v = `Center`
                    )->a( n = `class`        v = `sapUiTinyMarginEnd`

                    )->tag( `Avatar`
                        )->a( n = `src`          v = client->_bind( d_productpicurl )
                        )->a( n = `displaySize`  v = `S`
                        )->a( n = `displayShape` v = `Square`
                    )->tag( `Title`
                        )->a( n = `text`     v = client->_bind( d_name )
                        )->a( n = `wrapping` v = `true`
                        )->a( n = `class`    v = `sapUiTinyMarginEnd`

                )->end(
            )->end(
        )->end(
        )->ele( n = `navigationActions` ns = `uxap`
            )->tag( `OverflowToolbarButton`
                )->a( n = `id`      v = `enterFullScreenBtn`
                )->a( n = `type`    v = `Transparent`
                )->a( n = `icon`    v = `sap-icon://full-screen`
                )->a( n = `tooltip` v = `Enter Full Screen Mode`
                )->a( n = `visible` v = |\{= ${ client->_bind( layout ) } !== 'MidColumnFullScreen' \}|
                )->a( n = `press`   v = client->_event( `MID_FULL_SCREEN` )
            )->tag( `OverflowToolbarButton`
                )->a( n = `id`      v = `exitFullScreenBtn`
                )->a( n = `type`    v = `Transparent`
                )->a( n = `icon`    v = `sap-icon://exit-full-screen`
                )->a( n = `tooltip` v = `Exit Full Screen Mode`
                )->a( n = `visible` v = |\{= ${ client->_bind( layout ) } === 'MidColumnFullScreen' \}|
                )->a( n = `press`   v = client->_event( `MID_EXIT_FULL_SCREEN` )
            )->tag( `OverflowToolbarButton`
                )->a( n = `type`    v = `Transparent`
                )->a( n = `icon`    v = `sap-icon://decline`
                )->a( n = `tooltip` v = `Close middle column`
                )->a( n = `visible` v = |\{= ${ client->_bind( layout ) } !== 'OneColumn' \}|
                )->a( n = `press`   v = client->_event( `MID_CLOSE` )

        )->end(
        )->ele( n = `actions` ns = `uxap`
            )->tag( `Button`
                )->a( n = `text` v = `Edit`
                )->a( n = `type` v = `Emphasized`
            )->tag( `Button`
                )->a( n = `text` v = `Delete`
                )->a( n = `type` v = `Transparent`
            )->tag( `Button`
                )->a( n = `text` v = `Copy`
                )->a( n = `type` v = `Transparent`
            )->tag( `Button`
                )->a( n = `text` v = `Toggle Footer`
                )->a( n = `type` v = `Transparent`
            )->tag( `Button`
                )->a( n = `icon`    v = `sap-icon://action`
                )->a( n = `tooltip` v = `Share`
                )->a( n = `type`    v = `Transparent`

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

    detail->ele( n = `headerContent` ns = `uxap`
        )->ele( `FlexBox`
            )->a( n = `wrap`         v = `Wrap`
            )->a( n = `fitContainer` v = `true`
            )->a( n = `alignItems`   v = `Stretch`

            )->tag( `Avatar`
                )->a( n = `src`          v = client->_bind( d_productpicurl )
                )->a( n = `displaySize`  v = `L`
                )->a( n = `displayShape` v = `Square`
                )->a( n = `class`        v = `sapUiTinyMarginEnd`
            )->ele( `VBox`
                )->a( n = `justifyContent` v = `Center`
                )->a( n = `class`          v = `sapUiSmallMarginEnd`

                )->tag( `Label`
                    )->a( n = `text` v = `Main Category`
                )->tag( `Text`
                    )->a( n = `text` v = client->_bind( d_maincategory )

            )->end(
            )->ele( `VBox`
                )->a( n = `justifyContent` v = `Center`
                )->a( n = `class`          v = `sapUiSmallMarginEnd`

                )->tag( `Label`
                    )->a( n = `text` v = `Subcategory`
                )->tag( `Text`
                    )->a( n = `text` v = client->_bind( d_category )

            )->end(
            )->ele( `VBox`
                )->a( n = `justifyContent` v = `Center`
                )->a( n = `class`          v = `sapUiSmallMarginEnd`

                )->tag( `Label`
                    )->a( n = `text` v = `Price`
                )->tag( `ObjectNumber`
                    )->a( n = `number`     v = client->_bind( d_price )
                    )->a( n = `emphasized` v = `false`

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

    DATA(sections) = detail->ele( n = `sections` ns = `uxap` ).

    sections->ele( n = `ObjectPageSection` ns = `uxap`
        )->a( n = `title` v = `General Information`

        )->ele( n = `subSections` ns = `uxap`
            )->ele( n = `ObjectPageSubSection` ns = `uxap`

                )->ele( n = `blocks` ns = `uxap`
                    )->ele( n = `SimpleForm` ns = `form`
                        )->a( n = `maxContainerCols` v = `2`
                        )->a( n = `editable`         v = `false`
                        )->a( n = `layout`           v = `ResponsiveGridLayout`
                        )->a( n = `labelSpanL`       v = `12`
                        )->a( n = `labelSpanM`       v = `12`
                        )->a( n = `emptySpanL`       v = `0`
                        )->a( n = `emptySpanM`       v = `0`
                        )->a( n = `columnsL`         v = `1`
                        )->a( n = `columnsM`         v = `1`

                        )->ele( n = `content` ns = `form`
                            )->tag( `Label`
                                )->a( n = `text` v = `Product ID`
                            )->tag( `Text`
                                )->a( n = `text` v = client->_bind( d_productid )
                            )->tag( `Label`
                                )->a( n = `text` v = `Description`
                            )->tag( `Text`
                                )->a( n = `text` v = client->_bind( d_description )
                            )->tag( `Label`
                                )->a( n = `text` v = `Supplier`
                            )->tag( `Text`
                                )->a( n = `text` v = client->_bind( d_suppliername )

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

    sections->ele( n = `ObjectPageSection` ns = `uxap`
        )->a( n = `title` v = `Suppliers`

        )->ele( n = `subSections` ns = `uxap`
            )->ele( n = `ObjectPageSubSection` ns = `uxap`

                )->ele( n = `blocks` ns = `uxap`
                    )->ele( `Table`
                        )->a( n = `id`    v = `suppliersTable`
                        )->a( n = `mode`  v = `SingleSelectMaster`
                        )->a( n = `items` v = client->_bind( t_suppliers )
                        )->a( n = `itemPress` v = client->_event( val = `SUPPLIER_ITEM` arg = `${$parameters>/listItem}.getBindingContext().getProperty('TEXT')` )

                        )->ele( `columns`
                            )->tag( `Column`

                        )->end(
                        )->ele( `items`
                            )->ele( `ColumnListItem`
                                )->a( n = `type` v = `Navigation`

                                )->ele( `cells`
                                    )->tag( `ObjectIdentifier`
                                        )->a( n = `text` v = `{TEXT}`

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

    " DetailDetailDetail.view.xml - the end column
    fcl->ele( n = `endColumnPages` ns = `f`
        )->ele( n = `DynamicPage` ns = `f`
            )->a( n = `toggleHeaderOnTitleClick` v = `false`

            )->ele( n = `title` ns = `f`
                )->ele( n = `DynamicPageTitle` ns = `f`

                    )->ele( n = `heading` ns = `f`
                        )->ele( `FlexBox`
                            )->a( n = `wrap`         v = `Wrap`
                            )->a( n = `fitContainer` v = `true`
                            )->a( n = `alignItems`   v = `Center`

                            )->tag( `Title`
                                )->a( n = `text`     v = client->_bind( dd_text )
                                )->a( n = `wrapping` v = `true`
                                )->a( n = `class`    v = `sapUiTinyMarginEnd`

                        )->end(
                    )->end(
                    )->ele( n = `navigationActions` ns = `f`
                        )->tag( `OverflowToolbarButton`
                            )->a( n = `type`    v = `Transparent`
                            )->a( n = `icon`    v = `sap-icon://full-screen`
                            )->a( n = `tooltip` v = `Enter Full Screen Mode`
                            )->a( n = `visible` v = |\{= ${ client->_bind( layout ) } !== 'EndColumnFullScreen' \}|
                            )->a( n = `press`   v = client->_event( `END_FULL_SCREEN` )
                        )->tag( `OverflowToolbarButton`
                            )->a( n = `type`    v = `Transparent`
                            )->a( n = `icon`    v = `sap-icon://exit-full-screen`
                            )->a( n = `tooltip` v = `Exit Full Screen Mode`
                            )->a( n = `visible` v = |\{= ${ client->_bind( layout ) } === 'EndColumnFullScreen' \}|
                            )->a( n = `press`   v = client->_event( `END_EXIT_FULL_SCREEN` )
                        )->tag( `OverflowToolbarButton`
                            )->a( n = `type`    v = `Transparent`
                            )->a( n = `icon`    v = `sap-icon://decline`
                            )->a( n = `tooltip` v = `Close end column`
                            )->a( n = `press`   v = client->_event( `END_CLOSE` )

                    )->end(
                )->end(
            )->end(
            )->ele( n = `content` ns = `f`
                )->tag( `Text`
                    )->a( n = `text` v = `Information about Supplier`

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

    client->view_display( view->stringify( ) ).
    " Component.js: oProductsModel.setSizeLimit(1000) - the collection is 123 rows
    " and the JSONModel caps a bound aggregation at 100, so the table would stop 23
    " rows short of the count its own title reports
    client->follow_up_action( val   = client->cs_event-set_size_limit
                              t_arg = VALUE #( ( `1000` ) ( client->cs_view-main ) ) ).
    " The column position of a FlexibleColumnLayout is LIVE control state:
    " view_display( ) destroys the MAIN slot and XMLView.create builds a fresh
    " tree, so the begin column comes back on the first beginColumnPages entry
    " (categoriesPage) - while layout and t_rows are class state that survive.
    " Measured 2026-08-27: after a SEARCH the user was back on the categories
    " while the layout still claimed three columns and the filtered products
    " table was bound but off screen. Re-issuing the SAME page the CATEGORY_ITEM
    " branch last sent is the app 585 idiom; a view that has never navigated
    " parks nothing and issues nothing
    IF begin_page IS NOT INITIAL.
      client->follow_up_action( val   = client->cs_event-control_by_id
                                t_arg = VALUE #( ( `fcl` ) ( `to` ) ( begin_page ) ) ).
    ENDIF.

    " the original's router, app-owned: the hash carries the route the way
    " the manifest patterns spell it, and a hash change the app did not
    " write (browser Back/Forward, a manual edit) round-trips as
    " HASH_CHANGED. Re-asserted per render - it dies with an app switch
    client->follow_up_action( val   = client->cs_event-hash_attach_changed
                              t_arg = VALUE #( ( `HASH_CHANGED` ) ) ).

  ENDMETHOD.


  METHOD detail_bind.

    " Detail.controller's bindElement( '/ProductCollection/<n>' ) - the relative
    " bindings of the original resolve against the bound element, the port folds
    " them to root-seeded fields (app 229 idiom)
    ASSIGN t_products[ productid = productid ] TO FIELD-SYMBOL(<product>).
    IF sy-subrc <> 0.
      RETURN.
    ENDIF.
    d_name          = <product>-name.
    d_productid     = <product>-productid.
    d_maincategory  = <product>-maincategory.
    d_category      = <product>-category.
    d_suppliername  = <product>-suppliername.
    d_productpicurl = <product>-productpicurl.
    d_description   = <product>-description.
    d_price         = |{ <product>-currencycode } { <product>-price }|.

  ENDMETHOD.


  METHOD on_event.

    CASE client->get_event( ).

      WHEN `CATEGORY_ITEM`.
        " List.controller's onListItemPress: the begin column swaps to the
        " products page, filtered to the pressed category, and the mid column
        " opens on the FIRST product of it - navTo('detailDetail') with the
        " category name and that product's index into the FULL collection
        category_apply( client->get_event_arg( ) ).
        IF t_rows IS NOT INITIAL.
          detail_bind( t_rows[ 1 ]-productid ).
          READ TABLE t_products WITH KEY productid = t_rows[ 1 ]-productid TRANSPORTING NO FIELDS.
          IF sy-subrc = 0.
            product_ix = sy-tabix - 1.
          ENDIF.
        ENDIF.
        route  = `detailDetail`.
        layout = `TwoColumnsMidExpanded`.
        hash_push( ).

      WHEN `LIST_ITEM`.
        " Detail.controller's onListItemPress opens the mid column on that
        " product - the route carries its index into the FULL collection
        " (the bindingContext path index of the original)
        detail_bind( client->get_event_arg( ) ).
        READ TABLE t_products WITH KEY productid = client->get_event_arg( ) TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          product_ix = sy-tabix - 1.
        ENDIF.
        route  = `detailDetail`.
        layout = `TwoColumnsMidExpanded`.
        hash_push( ).

      WHEN `SUPPLIER_ITEM`.
        " handleItemPress: level 2 opens the end column
        dd_text = client->get_event_arg( ).
        READ TABLE t_suppliers WITH KEY text = dd_text TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          supplier_ix = sy-tabix - 1.
        ENDIF.
        route  = `detailDetailDetail`.
        layout = `ThreeColumnsMidExpanded`.
        hash_push( ).

      WHEN `MID_FULL_SCREEN`.
        route  = `detailDetail`.
        layout = `MidColumnFullScreen`.
        hash_push( ).

      WHEN `MID_EXIT_FULL_SCREEN`.
        route  = `detailDetail`.
        layout = `TwoColumnsMidExpanded`.
        hash_push( ).

      WHEN `MID_CLOSE`.
        " DetailDetail's handleClose leaves the products page alone in the
        " begin column - navigateToView('detail')
        route  = `detail`.
        layout = `OneColumn`.
        hash_push( ).

      WHEN `END_FULL_SCREEN`.
        route  = `detailDetailDetail`.
        layout = `EndColumnFullScreen`.
        hash_push( ).

      WHEN `END_EXIT_FULL_SCREEN`.
        route  = `detailDetailDetail`.
        layout = `ThreeColumnsMidExpanded`.
        hash_push( ).

      WHEN `END_CLOSE`.
        route  = `detailDetail`.
        layout = `TwoColumnsMidExpanded`.
        hash_push( ).

      WHEN `STATE_CHANGED`.
        " onStateChanged: the layout is a two-way binding, so the model
        " already carries the value this event reports - but when a
        " NAVIGATION ARROW changed it, the original replace-navTo's the
        " URL: same route, new layout, no new history entry
        IF client->get_event_arg( ) = abap_true.
          layout = client->get_event_arg( 2 ).
          hash_push( abap_true ).
        ENDIF.

      WHEN `HASH_CHANGED`.
        " browser Back/Forward (or a manual edit) moved the app-owned hash -
        " the router's routeMatched: derive route, category, indices and
        " layout from the hash this request carries. The instance itself is
        " untouched, so search text and sort order survive like in the
        " original
        hash_apply( client->get( )-s_config-hash ).

      WHEN `SEARCH`.
        " onSearch filters the table's items on Name
        DATA(query) = to_upper( client->get_event_arg( ) ).
        IF query IS INITIAL.
          t_rows = t_products.
        ELSE.
          CLEAR t_rows.
          LOOP AT t_products INTO DATA(product).
            IF to_upper( product-name ) CS query.
              APPEND product TO t_rows.
            ENDIF.
          ENDLOOP.
        ENDIF.

      WHEN `SORT`.
        " onSort flips the Name sorter; a thin frontend sorts the data it sends
        descending = xsdbool( descending = abap_false ).
        IF descending = abap_true.
          SORT t_rows BY name DESCENDING.
        ELSE.
          SORT t_rows BY name ASCENDING.
        ENDIF.

      WHEN `ADD`.
        " onAdd: MessageBox.show( 'This functionality is not ready yet.' )
        client->message_box_display( text  = `This functionality is not ready yet.`
                                     type  = `information`
                                     title = `Aw, Snap!` ).

    ENDCASE.

  ENDMETHOD.


  METHOD category_apply.

    " the category the URL (or the pressed row) names: filter the products
    " page to it and swap the begin column onto that page. Idempotent - the
    " same category leaves T_ROWS alone, so a search filtered further is not
    " reset by the next render's hash_apply
    IF iv_category = route_category AND begin_page IS NOT INITIAL.
      RETURN.
    ENDIF.
    route_category = iv_category.
    " the right-hand name of a WHERE resolves to the COLUMN, so the local
    " one must not share it (apps 520/524)
    DATA(sel_category) = iv_category.
    CLEAR t_rows.
    LOOP AT t_products INTO DATA(row) WHERE category = sel_category.
      APPEND row TO t_rows.
    ENDLOOP.
    " park it too, so a later view_display( ) can put the column back
    begin_page = `dynamicPageId`.
    client->follow_up_action( val   = client->cs_event-control_by_id
                              t_arg = VALUE #( ( `fcl` ) ( `to` ) ( begin_page ) ) ).

  ENDMETHOD.


  METHOD hash_apply.

    " the router's routeMatched, read side: parse the app hash back into
    " route, category, indices and layout. The original's patterns: ''
    " (list start), '{layout}' (the ':layout:' list route),
    " 'detail/{category}/{layout}',
    " 'detailDetail/{category}/{product}/{layout}',
    " 'detailDetailDetail/{category}/{product}/{supplier}/{layout}' -
    " the category is its NAME (spaces ride URL-encoded), product/supplier
    " are INDICES into the mock collections, defaulting to 0 like the
    " original's `arguments.product || this._product || "0"`
    DATA(lv_hash) = iv_hash.
    IF lv_hash CS `#`.
      lv_hash = substring_after( val = lv_hash sub = `#` ).
    ENDIF.
    SHIFT lv_hash LEFT DELETING LEADING `/`.
    SPLIT lv_hash AT `/` INTO TABLE DATA(lt_seg).
    DELETE lt_seg WHERE table_line IS INITIAL.

    DATA(lv_cat) = replace( val = VALUE string( lt_seg[ 2 ] OPTIONAL ) sub = `%20` with = ` ` occ = 0 ).
    DATA(lv_p)   = VALUE string( lt_seg[ 3 ] OPTIONAL ).
    DATA(lv_s)   = VALUE string( lt_seg[ 4 ] OPTIONAL ).

    CASE VALUE string( lt_seg[ 1 ] OPTIONAL ).
      WHEN ``.
        route  = `list`.
        layout = `OneColumn`.
        " back on the categories page - the list route targets List.view
        IF begin_page IS NOT INITIAL.
          CLEAR begin_page.
          CLEAR route_category.
          client->follow_up_action( val   = client->cs_event-control_by_id
                                    t_arg = VALUE #( ( `fcl` ) ( `to` ) ( `categoriesPage` ) ) ).
        ENDIF.

      WHEN `detail`.
        route = `detail`.
        category_apply( lv_cat ).
        layout = COND #( WHEN lv_p IS NOT INITIAL THEN lv_p ELSE `OneColumn` ).

      WHEN `detailDetail`.
        route = `detailDetail`.
        category_apply( lv_cat ).
        product_ix = COND #( WHEN lv_p CO `0123456789` AND lv_p IS NOT INITIAL AND strlen( lv_p ) <= 4 THEN lv_p ).
        layout     = COND #( WHEN lv_s IS NOT INITIAL THEN lv_s ELSE `TwoColumnsMidExpanded` ).
        IF product_ix < lines( t_products ).
          detail_bind( t_products[ product_ix + 1 ]-productid ).
        ENDIF.

      WHEN `detailDetailDetail`.
        route = `detailDetailDetail`.
        category_apply( lv_cat ).
        product_ix  = COND #( WHEN lv_p CO `0123456789` AND lv_p IS NOT INITIAL AND strlen( lv_p ) <= 4 THEN lv_p ).
        supplier_ix = COND #( WHEN lv_s CO `0123456789` AND lv_s IS NOT INITIAL AND strlen( lv_s ) <= 4 THEN lv_s ).
        layout      = COND #( WHEN VALUE string( lt_seg[ 5 ] OPTIONAL ) IS NOT INITIAL
                              THEN lt_seg[ 5 ]
                              ELSE `ThreeColumnsMidExpanded` ).
        IF product_ix < lines( t_products ).
          detail_bind( t_products[ product_ix + 1 ]-productid ).
        ENDIF.
        IF supplier_ix < lines( t_suppliers ).
          dd_text = t_suppliers[ supplier_ix + 1 ]-text.
        ENDIF.

      WHEN OTHERS.
        " the single-segment ':layout:' list route, e.g. '#/OneColumn'
        route  = `list`.
        layout = lt_seg[ 1 ].
        IF begin_page IS NOT INITIAL.
          CLEAR begin_page.
          CLEAR route_category.
          client->follow_up_action( val   = client->cs_event-control_by_id
                                    t_arg = VALUE #( ( `fcl` ) ( `to` ) ( `categoriesPage` ) ) ).
        ENDIF.
    ENDCASE.

  ENDMETHOD.


  METHOD hash_push.

    DATA lv_hash TYPE string.
    " the router's navTo, write side: compose the current route the way the
    " manifest patterns spell it and push it as the app-owned hash. The
    " category name is URL-encoded the way the original's navTo encodes it
    DATA(lv_cat) = replace( val = route_category sub = ` ` with = `%20` occ = 0 ).
    CASE route.
      WHEN `detail`.
        lv_hash = |/detail/{ lv_cat }/{ layout }|.
      WHEN `detailDetail`.
        lv_hash = |/detailDetail/{ lv_cat }/{ product_ix }/{ layout }|.
      WHEN `detailDetailDetail`.
        lv_hash = |/detailDetailDetail/{ lv_cat }/{ product_ix }/{ supplier_ix }/{ layout }|.
      WHEN OTHERS.
        lv_hash = |/{ layout }|.
    ENDCASE.

    " a NAVIGATION ARROW rewrites the URL in place (the original's
    " replace-navTo) - everything else is a real, pushed history entry
    IF check_replace = abap_true.
      client->hash_replace( lv_hash ).
    ELSE.
      client->hash_set( lv_hash ).
    ENDIF.

  ENDMETHOD.


  METHOD model_init.

    " the full mock /ProductCollection, in the mock order - the items binding
    " carries NO sorter, so the backend owns the order. Do not add one: a
    " declared sorter is re-applied by JSONListBinding.update on every model
    " change (ClientListBinding.applySort), so it becomes the primary key and
    " the ABAP order survives only as a tiebreak - which is exactly what made
    " the Sort button unable to sort here
    t_products = VALUE #(
      ( productid = `HT-1000` name = `Notebook Basic 15`
        maincategory = `Computer Systems` category = `Laptops` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1000.jpg`
        description = `Notebook Basic 15 with 2,80 GHz quad core, 15" LCD, 4 GB DDR3 RAM, 500 GB Hard Disc, Windows 8 Pro` price = `956` currencycode = `EUR` )
      ( productid = `HT-1001` name = `Notebook Basic 17`
        maincategory = `Computer Systems` category = `Laptops` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1001.jpg`
        description = `Notebook Basic 17 with 2,80 GHz quad core, 17" LCD, 4 GB DDR3 RAM, 500 GB Hard Disc, Windows 8 Pro` price = `1249` currencycode = `EUR` )
      ( productid = `HT-1002` name = `Notebook Basic 18`
        maincategory = `Computer Systems` category = `Laptops` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1002.jpg`
        description = `Notebook Basic 18 with 2,80 GHz quad core, 18" LCD, 8 GB DDR3 RAM, 1000 GB Hard Disc, Windows 8 Pro` price = `1570` currencycode = `EUR` )
      ( productid = `HT-1003` name = `Notebook Basic 19`
        maincategory = `Computer Systems` category = `Laptops` suppliername = `Smartcards` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1003.jpg`
        description = `Notebook Basic 19 with 2,80 GHz quad core, 19" LCD, 8 GB DDR3 RAM, 1000 GB Hard Disc, Windows 8 Pro` price = `1650` currencycode = `EUR` )
      ( productid = `HT-1007` name = `ITelO Vault`
        maincategory = `Computer Components` category = `Accessories` suppliername = `Technocom` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1007.jpg`
        description = `Digital Organizer with State-of-the-Art Storage Encryption` price = `299` currencycode = `EUR` )
      ( productid = `HT-1010` name = `Notebook Professional 15`
        maincategory = `Computer Systems` category = `Accessories` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1010.jpg`
        description = `Notebook Professional 15 with 2,80 GHz quad core, 15" Multitouch LCD, 8 GB DDR3 RAM, 500 GB SSD - DVD-Writer (DVD-R/+R/-RW/-RAM),Windows 8 Pro` price = `1999` currencycode = `EUR` )
      ( productid = `HT-1011` name = `Notebook Professional 17`
        maincategory = `Computer Systems` category = `Laptops` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1011.jpg`
        description = `Notebook Professional 17 with 2,80 GHz quad core, 17" Multitouch LCD, 8 GB DDR3 RAM, 500 GB SSD - DVD-Writer (DVD-R/+R/-RW/-RAM),Windows 8 Pro` price = `2299` currencycode = `EUR` )
      ( productid = `HT-1020` name = `ITelO Vault Net`
        maincategory = `Computer Components` category = `Accessories` suppliername = `Technocom` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1020.jpg`
        description = `Digital Organizer with State-of-the-Art Encryption for Storage and Network Communications` price = `459` currencycode = `EUR` )
      ( productid = `HT-1021` name = `ITelO Vault SAT`
        maincategory = `Computer Components` category = `Accessories` suppliername = `Technocom` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1021.jpg`
        description = `Digital Organizer with State-of-the-Art Encryption for Storage and Secure Stellite Link` price = `149` currencycode = `EUR` )
      ( productid = `HT-1022` name = `Comfort Easy`
        maincategory = `Computer Components` category = `Accessories` suppliername = `Technocom` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1022.jpg`
        description = `32 GB Digital Assistant with high-resolution color screen` price = `1679` currencycode = `EUR` )
      ( productid = `HT-1023` name = `Comfort Senior`
        maincategory = `Computer Components` category = `Accessories` suppliername = `Technocom` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1023.jpg`
        description = `64 GB Digital Assistant with high-resolution color screen and synthesized voice output` price = `512` currencycode = `EUR` )
      ( productid = `HT-1030` name = `Ergo Screen E-I`
        maincategory = `Computer Components` category = `Flat Screen Monitors` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1030.jpg`
        description = `Optimum Hi-Resolution max. 1920 x 1080 @ 85Hz, Dot Pitch: 0.27mm` price = `230` currencycode = `EUR` )
      ( productid = `HT-1031` name = `Ergo Screen E-II`
        maincategory = `Computer Components` category = `Flat Screen Monitors` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1031.jpg`
        description = `Optimum Hi-Resolution max. 1920 x 1200 @ 85Hz, Dot Pitch: 0.26mm` price = `285` currencycode = `EUR` )
      ( productid = `HT-1032` name = `Ergo Screen E-III`
        maincategory = `Computer Components` category = `Flat Screen Monitors` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1032.jpg`
        description = `Optimum Hi-Resolution max. 2560 x 1440 @ 85Hz, Dot Pitch: 0.25mm` price = `345` currencycode = `EUR` )
      ( productid = `HT-1035` name = `Flat Basic`
        maincategory = `Computer Components` category = `Flat Screen Monitors` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1035.jpg`
        description = `Optimum Hi-Resolution max. 1600 x 1200 @ 85Hz, Dot Pitch: 0.24mm` price = `399` currencycode = `EUR` )
      ( productid = `HT-1036` name = `Flat Future`
        maincategory = `Computer Components` category = `Flat Screen Monitors` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1036.jpg`
        description = `Optimum Hi-Resolution max. 2048 x 1080 @ 85Hz, Dot Pitch: 0.26mm` price = `430` currencycode = `EUR` )
      ( productid = `HT-1037` name = `Flat XL`
        maincategory = `Computer Components` category = `Flat Screen Monitors` suppliername = `Very Best Screens` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1037.jpg`
        description = `Optimum Hi-Resolution max. 2016 x 1512 @ 85Hz, Dot Pitch: 0.24mm` price = `1230` currencycode = `EUR` )
      ( productid = `HT-1040` name = `Laser Professional Eco`
        maincategory = `Printers & Scanners` category = `Printers` suppliername = `Alpha Printers` productpicurl = `https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1040.jpg`
        description = `Print 2400 dpi image quality color documents at speeds of up to 32 ppm (color) or 36 ppm (monochrome), letter/A4. Powerful 500 MHz processor, 512MB of memory` price = `830` currencycode = `EUR` )

The first 900 lines of 1253 — the whole class is on GitHub.

More in sap.f

Search every abap2UI5 sample · the full list on one page

On this page