Flexible Column Layout with programmatic scrolling to an item
FlexibleColumnLayout where the app programmatically scrolls to some item within the newly navigated column, once the column is fully resized
Z2UI5_CL_SMPC_APP_577
Controls
sap.f
UI5 1.77
The facts
- Repository
- abap2UI5/samples-controls
- Class
Z2UI5_CL_SMPC_APP_577- Source file
z2ui5_cl_smpc_app_577.clas.abap↗- Library
- sap.f
- UI5 entity
sap.f.FlexibleColumnLayout- Demo kit sample
sap.f.sample.FlexibleColumnLayoutColumnResize- SAP's own sample
- running at sdk.openui5.org ↗
- Minimum UI5
- 1.77
- In the playground
- runs in the browser, with no system and nothing installed
Keywords: flexiblecolumnlayout, flexible, column, layout, sap.f, flexiblecolumnlayoutcolumnresize, objectpagelayout, objectpagedynamicheadertitle, title, objectpagesection, objectpagesubsection, button
Controls it builds
- sap.f.DynamicPage
- sap.f.DynamicPageHeader
- sap.f.DynamicPageTitle
- sap.f.FlexibleColumnLayout
- sap.m.Button
- sap.m.Label
- sap.m.MessageStrip
- sap.m.ObjectAttribute
- sap.m.ObjectStatus
- sap.m.Text
- sap.m.Title
- sap.ui.core.mvc.View
- sap.ui.layout.HorizontalLayout
- sap.ui.layout.VerticalLayout
- sap.ui.layout.form.SimpleForm
- sap.uxap.ObjectPageDynamicHeaderTitle
- sap.uxap.ObjectPageLayout
- sap.uxap.ObjectPageSection
- sap.uxap.ObjectPageSubSection
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_577.clas.abap — 393 lines
Click a number to link to a line — shift-click for a range.
Read it on GitHub ↗
" @keywords flexiblecolumnlayout flexible column layout sap.f flexiblecolumnlayoutcolumnresize objectpagelayout objectpagedynamicheadertitle title objectpagesection objectpagesubsection button
" @summary FlexibleColumnLayout where the app programmatically scrolls to some item within the newly navigated column, once the column is fully resized
CLASS z2ui5_cl_smpc_app_577 DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES z2ui5_if_app.
TYPES: BEGIN OF ty_s_section,
tablename TYPE string,
sectionname TYPE string,
END OF ty_s_section.
TYPES ty_t_section TYPE STANDARD TABLE OF ty_s_section WITH EMPTY KEY.
DATA t_sections TYPE ty_t_section.
" the FlexibleColumnLayout state the router drives in the original
DATA layout TYPE string VALUE `OneColumn`.
PROTECTED SECTION.
DATA client TYPE REF TO z2ui5_if_client.
" the router state the original keeps: the current route and the section
" index the ':section:' list route carries (this.iSectionIndex, NaN when
" the URL names none - here -1)
DATA route TYPE string VALUE `list`.
DATA section_ix TYPE i VALUE -1.
METHODS view_display.
METHODS on_event.
METHODS hash_apply IMPORTING iv_hash TYPE string.
METHODS hash_push IMPORTING check_replace TYPE abap_bool OPTIONAL.
METHODS section_select.
METHODS model_init.
PRIVATE SECTION.
ENDCLASS.
CLASS z2ui5_cl_smpc_app_577 IMPLEMENTATION.
METHOD z2ui5_if_app~main.
me->client = client.
IF client->check_on_init( ).
model_init( ).
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 (`#/3`, `#/detail/
" MidColumnFullScreen`): 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:layout` v = `sap.ui.layout`
)->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 = `autoFocus` v = `false`
)->a( n = `restoreFocusOnBackNavigation` v = `true`
)->a( n = `backgroundDesign` v = `Translucent`
" List.controller attaches columnResize on the FCL: once the begin
" column is back to full width the indexed section is re-selected
)->a( n = `columnResize` v = client->_event( val = `COLUMN_RESIZE` arg = `${$parameters>/beginColumn}` )
" 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 ObjectPage whose sections come from the model.
" The original attaches the ObjectPage's navigate event to
" _updateUrlOnNavigate, which writes indexOfSection( section ) into the
" ':section:' route - the index is computed where the control lives
fcl->ele( n = `beginColumnPages` ns = `f`
)->ele( n = `ObjectPageLayout` ns = `uxap`
)->a( n = `id` v = `ObjectPageLayout`
)->a( n = `upperCaseAnchorBar` v = `false`
)->a( n = `navigate` v = client->_event( val = `NAVIGATE` arg = `${$parameters>/section}.getParent().indexOfSection(${$parameters>/section})` )
)->a( n = `sections` v = client->_bind( t_sections )
)->ele( n = `headerTitle` ns = `uxap`
)->ele( n = `ObjectPageDynamicHeaderTitle` ns = `uxap`
)->ele( n = `heading` ns = `uxap`
)->tag( `Title`
)->a( n = `text` v = `Sections`
)->end(
)->end(
)->end(
)->ele( n = `sections` ns = `uxap`
)->ele( n = `ObjectPageSection` ns = `uxap`
)->a( n = `title` v = `{SECTIONNAME}`
)->ele( n = `subSections` ns = `uxap`
)->ele( n = `ObjectPageSubSection` ns = `uxap`
)->ele( n = `actions` ns = `uxap`
)->tag( `Button`
)->a( n = `text` v = `To Detail`
)->a( n = `press` v = client->_event( `TO_DETAIL` )
)->end(
)->ele( n = `blocks` ns = `uxap`
)->ele( n = `SimpleForm` ns = `form`
)->a( n = `editable` v = `false`
)->a( n = `layout` v = `ColumnLayout`
)->tag( `Label`
)->a( n = `text` v = `Content`
)->tag( `Text`
)->a( n = `text` v = `some content goes here...`
)->end(
)->end(
)->end(
)->end(
)->end(
)->end(
)->end(
)->end( ).
" Detail.view.xml - the DynamicPage of the mid column
fcl->ele( n = `midColumnPages` ns = `f`
)->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 = `Details Page`
)->end(
)->ele( n = `navigationActions` ns = `f`
)->tag( `Button`
)->a( n = `icon` v = `sap-icon://decline`
)->a( n = `tooltip` v = `Close column`
)->a( n = `type` v = `Transparent`
)->a( n = `press` v = client->_event( `CLOSE_COLUMN` )
)->end(
)->end(
)->end(
)->ele( n = `header` ns = `f`
)->ele( n = `DynamicPageHeader` ns = `f`
)->a( n = `pinnable` v = `false`
)->ele( n = `HorizontalLayout` ns = `layout`
)->a( n = `allowWrapping` v = `true`
)->ele( n = `VerticalLayout` ns = `layout`
)->a( n = `class` v = `sapUiMediumMarginEnd`
)->tag( `ObjectAttribute`
)->a( n = `title` v = `Location`
)->a( n = `text` v = `Warehouse A`
)->tag( `ObjectAttribute`
)->a( n = `title` v = `Halway`
)->a( n = `text` v = `23L`
)->tag( `ObjectAttribute`
)->a( n = `title` v = `Rack`
)->a( n = `text` v = `34`
)->end(
)->ele( n = `VerticalLayout` ns = `layout`
)->tag( `ObjectAttribute`
)->a( n = `title` v = `Availability`
)->tag( `ObjectStatus`
)->a( n = `text` v = `In Stock`
)->a( n = `state` v = `Success`
)->end(
)->end(
)->end(
)->end(
)->ele( n = `content` ns = `f`
)->tag( `MessageStrip`
)->a( n = `type` v = `Success`
)->a( n = `text` v = `Close this column to return to the previous page and resume its scroll position`
)->end(
)->end(
)->end( ).
client->view_display( view->stringify( ) ).
" 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` ) ) ).
" _onListMatched on the first rendering: a deep link '#/3' selects the
" indexed section. A rebuilt ObjectPage is back on its first section while
" section_ix survives as class state, so the re-issue also restores what
" the original's live control keeps natively
IF section_ix >= 0.
section_select( ).
ENDIF.
ENDMETHOD.
METHOD section_select.
" setSelectedSection takes a section INSTANCE (an association); the bound
" sections are runtime clones, addressed positionally as an aggregation
" item - <id>/<aggregation>/<index>, resolved on the client where the ids
" are known
client->follow_up_action( val = client->cs_event-control_by_id
t_arg = VALUE #( ( `ObjectPageLayout` )
( `setSelectedSection` )
( |ObjectPageLayout/sections/{ section_ix }| ) ) ).
ENDMETHOD.
METHOD on_event.
CASE client->get_event( ).
WHEN `TO_DETAIL`.
" toDetail: navTo('detail') with the helper's next layout for level 1
route = `detail`.
layout = `MidColumnFullScreen`.
hash_push( ).
WHEN `CLOSE_COLUMN`.
" handleClose: navTo('list') with the helper's closeColumn layout -
" the ':section:' pattern carries no layout and no section, so the
" URL goes back to the bare start (and iSectionIndex to NaN, here -1)
route = `list`.
layout = `OneColumn`.
section_ix = -1.
hash_push( ).
WHEN `NAVIGATE`.
" _updateUrlOnNavigate: the anchor-bar selection writes the section
" index into the URL
DATA(lv_ix) = client->get_event_arg( ).
IF lv_ix CO `0123456789` AND lv_ix IS NOT INITIAL AND strlen( lv_ix ) <= 4.
section_ix = lv_ix.
route = `list`.
hash_push( ).
ENDIF.
WHEN `COLUMN_RESIZE`.
" onColumnResize: when the begin column has grown back to one column,
" re-select the section the URL names - the original's
" _scrollToIndexedSection
IF client->get_event_arg( ) = abap_true
AND layout = `OneColumn`
AND section_ix >= 0.
section_select( ).
ENDIF.
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, section index and layout
" from the hash this request carries. Like the original, a hash
" change alone does not re-scroll the list (only the first rendering
" and the column resize do)
hash_apply( client->get( )-s_config-hash ).
ENDCASE.
ENDMETHOD.
METHOD hash_apply.
" the router's routeMatched, read side. The original's patterns:
" '' (list start), '{section}' (the ':section:' list route, a section
" INDEX), 'detail/{layout}'
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_1) = VALUE string( lt_seg[ 1 ] OPTIONAL ).
DATA(lv_2) = VALUE string( lt_seg[ 2 ] OPTIONAL ).
CASE lv_1.
WHEN ``.
route = `list`.
layout = `OneColumn`.
section_ix = -1.
WHEN `detail`.
route = `detail`.
layout = COND #( WHEN lv_2 IS NOT INITIAL THEN lv_2 ELSE `MidColumnFullScreen` ).
WHEN OTHERS.
" the single-segment ':section:' list route, e.g. '#/3'
route = `list`.
layout = `OneColumn`.
IF lv_1 CO `0123456789` AND strlen( lv_1 ) <= 4.
section_ix = lv_1.
ELSE.
section_ix = -1.
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 bare
" list route pushes '/' - the leading slash is normalised away, so the
" URL carries an empty app hash exactly like the original's
" navTo('list') without a section
CASE route.
WHEN `detail`.
lv_hash = |/detail/{ layout }|.
WHEN OTHERS.
IF section_ix >= 0.
lv_hash = |/{ section_ix }|.
ELSE.
lv_hash = `/`.
ENDIF.
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.
" webapp/data/sections.json - the twelve sections
t_sections = VALUE #(
( tablename = `Navigate to section 0` sectionname = `Section 0` )
( tablename = `Navigate to section 1` sectionname = `Section 1` )
( tablename = `Navigate to section 2` sectionname = `Section 2` )
( tablename = `Navigate to section 3` sectionname = `Section 3` )
( tablename = `Navigate to section 4` sectionname = `Section 4` )
( tablename = `Navigate to section 5` sectionname = `Section 5` )
( tablename = `Navigate to section 6` sectionname = `Section 6` )
( tablename = `Navigate to section 7` sectionname = `Section 7` )
( tablename = `Navigate to section 8` sectionname = `Section 8` )
( tablename = `Navigate to section 9` sectionname = `Section 9` )
( tablename = `Navigate to section 10` sectionname = `Section 10` )
( tablename = `Navigate to section 11` sectionname = `Section 11` ) ).
ENDMETHOD.
ENDCLASS.
More in sap.f
- Grid Container - Drag and Drop — This sample represents GridContainer with enabled Drag and Drop functionality.
- Grid Container - Drag and Drop From List to GridContainer — This sample represents how items from a control which is not GridContainer can be dragged…
- Grid Container - Keyboard navigation between multiple grids — This sample demonstrates the keyboard navigation between multiple grids
- Semantic Page - Freestyle Sample — This sample demonstrates a SemanticPage with all semantic-specific actions both in the ti…
- Dynamic Page with Analytical Table — Dynamic Page containing an Analytical Table in the content area aligned with the SAP Fior…
- Dynamic Page with Wizard — Dynamic Page containing a sap.m.Wizard and WizardProgressNavigator in the content area.
- 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 initia…
- Flexible Column Layout with Routing and One Initial Column — Flexible Column Layout as an app with routing that starts with a single initial column.
- Flexible Column Layout with Routing and Two Initial Columns — Flexible Column Layout as an app with routing that starts with two initial columns.
- Grid List - Item Designs — This is a sample for GridList item templates representing a typical tools page dashboard…
- Navigation with keyboard arrows between multiple GridLists — This sample demonstrates the keyboard navigation between multiple grids
- Shell Bar with Product Switch — Shell Bar example with enabled Product Switch, configurable by the app developer. The Pro…
On this page