You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**TITLE**: Custom Page with Timeline Control Navigation from List Report Toolbar
954
+
**TITLE**: Custom Page with Timeline Control Navigation from List Report Toolbar ✅ VERIFIED WORKING PATTERN
955
955
956
-
**INTRODUCTION**: This guide explains how to add a "Show Timeline" button to the List Report table toolbar that navigates to a custom page. The custom page displays all travel records in a Timeline control (sap.suite.ui.commons.Timeline) sorted ascending by BeginDate. The custom page uses the sap.fe.macros.Page building block, a sap.m.Panel, and the Timeline control. This pattern demonstrates how to implement outbound navigation to a fully custom view in a Fiori Elements application.
956
+
**INTRODUCTION**: This guide explains how to add a "Show Timeline" button to the List Report table toolbar that navigates to a custom FPM page. The custom page displays all travel records in a `sap.suite.ui.commons.Timeline` control sorted ascending by `BeginDate`. The custom page uses `sap.fe.macros.Page` as the container, `sap.m.Panel` as a wrapper, and `sap.suite.ui.commons.Timeline` with inline `TimelineItem` binding. This pattern was verified end-to-end including mock server and live OData V4. Follow every critical pitfall — each one causes silent failures or runtime errors.
957
957
958
-
**TAGS**: custom page, timeline, navigation, toolbar button, List Report, sap.fe.macros.Page, Page building block, sap.m.Panel, sap.suite.ui.commons.Timeline, TimelineItem, controller extension, custom route, manifest routing, extension, building blocks, custom navigation
958
+
Replace all placeholder names with your actual values:
959
+
-`my.app.namespace` → your app namespace (e.g. `fin.test.rap.lr3`)
960
+
-`EntitySet` → your OData entity set name (e.g. `Travel`)
961
+
-`ListReportTarget` → your List Report routing target name
962
+
-`TimelinePageTarget` → your timeline page routing target name
963
+
-`TimelinePageRoute` → your timeline route name (e.g. `TravelTimeline`)
964
+
965
+
**TAGS**: custom page, timeline, navigation, toolbar button, List Report, sap.fe.macros.Page, Page building block, sap.m.Panel, sap.suite.ui.commons.Timeline, TimelineItem, controller extension, custom route, manifest routing, extension, building blocks, custom navigation, FPM, HashChanger, press handler, module ID
966
+
967
+
---
968
+
969
+
**CRITICAL PITFALL A — The manifest `press` handler for toolbar actions resolves a PLAIN `.js` module, NOT a `.controller.js` module**: When a custom toolbar action is declared in `manifest.json` under `controlConfiguration["@com.sap.vocabularies.UI.v1.LineItem"].actions` with `"press": "my.app.namespace.ext.controller.ListReportExt.onShowTimeline"`, the Fiori Elements framework resolves this by loading the AMD module `my/app/namespace/ext/controller/ListReportExt.js` (note: plain `.js`, NOT `ListReportExt.controller.js`). The `ListReportExt.controller.js` file is registered under a different module ID: `my/app/namespace/ext/controller/ListReportExt.controller`. Attempting to use only `ListReportExt.controller.js` causes `ModuleError: failed to load 'my/app/namespace/ext/controller/ListReportExt.js'` and the button click does nothing. **Fix**: Create a SEPARATE file `webapp/ext/controller/ListReportExt.js` (no `.controller.` in the filename) that exports a plain object with the handler function. This file coexists with `ListReportExt.controller.js`.
970
+
971
+
**CRITICAL PITFALL B — In a plain FPM module handler (`.js`, not `.controller.js`), `this` is the exported object — use `HashChanger` for navigation, NOT the router**: In a plain AMD module (the `.js` file described in Pitfall A), the exported handler function is called with `this` set to the exported object itself — there is no SAPUI5 component or controller context. `UIComponent.getRouterFor(this)` returns `undefined` because `this` is not a ManagedObject. Calling `this.base.getView().getController().getOwnerComponent().getRouter()` crashes with `TypeError: Cannot read properties of undefined`. **Fix**: Use `sap.ui.core.routing.HashChanger.getInstance().setHash("RoutePattern")` to navigate. This directly sets the browser hash (triggering the router's pattern matching) without needing a component or controller reference. Use the route PATTERN (not the route name) as the hash value. For example, if the route has `"pattern": "TravelTimeline:?query:"`, set the hash to `"TravelTimeline"` (omit the `:?query:` optional query suffix).
959
972
960
-
**STEP**: Add custom route and target for the Timeline page in manifest.json
973
+
**CRITICAL PITFALL C — Route pattern must be unique**: The timeline route pattern must not conflict with any existing route pattern. In Fiori Elements apps, the List Report route typically uses `":?query:"` and the Object Page route uses `"EntitySet({key}):?query:"`. Use a distinct string prefix (e.g. `"TravelTimeline:?query:"`) that cannot match those patterns. Do NOT use a pattern like `"Travel({key}):?query:"` — it would conflict with the Object Page route.
974
+
975
+
**CRITICAL PITFALL D — MCP tools may overwrite `navigation.EntitySet.detail.route` when modifying manifest.json**: Some automated tooling (including MCP-based code generators) may regenerate the manifest routing section and reset `navigation.EntitySet.detail.route` from your custom timeline target back to the Object Page. After any automated manifest modification, verify that the `navigation` section still maps row clicks to `TravelObjectPage` (or whatever your Object Page target is), NOT to the timeline target. The timeline target should only be reachable via the toolbar button.
976
+
977
+
**CRITICAL PITFALL E — The FPM custom page target must use `sap.fe.core.fpm` component, NOT `sap.fe.templates.ListReport` or a plain View target**: When the timeline page target is registered in manifest.json, it must use `"name": "sap.fe.core.fpm"` as the component name (not a view type/name directly at the target level). The `viewName` goes inside `options.settings`. This is required for `sap.fe.macros.Page` to work as the root element of the view. A plain `"type": "View"` target does NOT initialise the FPM context needed by `macros:Page`.
978
+
979
+
**CRITICAL PITFALL F — `TimelineItem` controls inside a bound `Timeline` need an explicit `id` attribute when `flexEnabled: true`**: When `sap.ui5.flexEnabled: true` is set in manifest.json, all controls need stable IDs for SAPUI5 Flexibility. Add `id="timelineItemTemplate"` (or similar) to the `suite:TimelineItem` element inside the Timeline's content aggregation binding template.
980
+
981
+
---
961
982
962
-
**DESCRIPTION**: Add a new route and target in the manifest.json routing configuration to define the custom timeline page. The target references a custom XML view.
983
+
**STEP 1**: Add the timeline route and FPM target to manifest.json routing
984
+
985
+
**DESCRIPTION**: Add a new route with a unique pattern and a new target using `sap.fe.core.fpm`. The target's `options.settings` must include `contextPath` (the OData entity set path) and `viewName` (the fully qualified view module name). The `navigation` section must keep the List Report row-click navigation pointing to the Object Page, NOT to the timeline target.
**STEP 2**: Register the toolbar action in manifest.json
991
1056
992
-
**ADDITIONAL RELATED CODE BLOCKS**:
1057
+
**DESCRIPTION**: Add the `showTimeline` action under `controlConfiguration["@com.sap.vocabularies.UI.v1.LineItem"].actions` in the List Report target settings. The `press` value must reference `my.app.namespace.ext.controller.ListReportExt.onShowTimeline` — where `ListReportExt` (without `.controller.`) is the plain module file created in Step 4 (see Pitfall A).
**STEP 3**: Add `sap.suite.ui.commons` library dependency to manifest.json
1080
+
1081
+
**DESCRIPTION**: The Timeline control comes from `sap.suite.ui.commons`. Add it to `sap.ui5.dependencies.libs` with `"lazy": false` so it is loaded upfront.
1082
+
1083
+
**FILE**: webapp/manifest.json
1084
+
1085
+
**LANGUAGE**: JSON
1086
+
1087
+
**CODE**:
1088
+
```JSON
1089
+
"sap.ui5": {
1090
+
"dependencies": {
1091
+
"libs": {
1092
+
"sap.m": {},
1093
+
"sap.ui.core": {},
1094
+
"sap.fe.templates": {},
1095
+
"sap.suite.ui.commons": {
1096
+
"lazy": false
1097
+
}
1098
+
}
1099
+
}
1100
+
}
1101
+
```
1102
+
1103
+
**STEP 4**: Create the plain press-handler module `ListReportExt.js`
1104
+
1105
+
**DESCRIPTION**: This is a SEPARATE file from `ListReportExt.controller.js`. It exports a plain object (not a ControllerExtension) with the `onShowTimeline` method. FE resolves `my.app.namespace.ext.controller.ListReportExt.onShowTimeline` by loading `ListReportExt.js` (no `.controller.` in filename — see Pitfall A). Use `HashChanger.getInstance().setHash(...)` for navigation — do NOT use the router (see Pitfall B). The hash value is the route pattern prefix WITHOUT the `:?query:` optional part.
1106
+
1107
+
**FILE**: webapp/ext/controller/ListReportExt.js
1019
1108
1020
1109
**LANGUAGE**: JavaScript
1021
1110
1022
1111
**CODE**:
1023
1112
```JavaScript
1024
-
sap.ui.define([
1025
-
"sap/ui/core/mvc/ControllerExtension"
1026
-
], function (ControllerExtension) {
1113
+
sap.ui.define(["sap/ui/core/routing/HashChanger"], function (HashChanger) {
**DESCRIPTION**: The view uses `sap.fe.macros.Page` as the root element (required for FPM context — Pitfall E). The `macros:Page` wraps a `sap.m.Panel` which contains the `sap.suite.ui.commons.Timeline`. The Timeline uses an aggregation binding on `/EntitySet` with `$orderby: 'BeginDate asc'` to fetch records sorted ascending. The `suite:TimelineItem` template has an explicit `id` attribute (required when `flexEnabled: true` — Pitfall F). The `controllerName` is required and must point to a valid controller file.
**STEP 7**: Verify navigation still works for row clicks (Object Page)
1199
+
1200
+
**DESCRIPTION**: After completing all steps above, verify that clicking a table row in the List Report still navigates to the Object Page (not the timeline). In manifest.json, check that `navigation.EntitySet.detail.route` is set to the Object Page route name (e.g. `EntitySetObjectPage`), NOT to `TravelTimelinePage`. If any automated tooling was used on manifest.json, re-verify this (Pitfall D).
0 commit comments