SAPUI5 and SAP Fiori Tools
RAP / CDS / ODataintermediate

SAPUI5 and Fiori Tools Part 4: Routing, Navigation, Fragments and Dialogs

Key takeaway

Add navigation to a freestyle SAPUI5 app: configure routes and targets in manifest. json, navigate with parameters, read them in the target view, and reuse UI with fragments and dialogs.

Explanation

📌 KEY POINTS
✅ Routes (URL patterns) and targets (views) are configured in manifest.json — not in code.

✅ Navigate with oRouter.navTo("detail", { requestId }) and read parameters in attachPatternMatched.

✅ Fragments are reusable UI pieces without their own controller; dialogs are usually fragments.

⚠️ Always bind the detail view in the pattern-matched handler, not in onInit.

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Parts 1–3 built views, models and binding. Real apps have several pages. This part adds a list → detail navigation and a dialog to our purchase request app.

━━━━━━━━━━━━━━━━━━━━
▶ 1. ROUTING CONCEPTS

  • Router — reads the URL hash and shows the right view.
  • Route — a name and a URL pattern, e.g. "detail" with pattern "requests/{requestId}".
  • Target — which view to display and where (control aggregation of the app container).
  • The URL hash (#/requests/4711) makes pages bookmarkable and supports browser back.

━━━━━━━━━━━━━━━━━━━━
▶ 2. CONFIGURATION IN MANIFEST.JSON

Under "sap.ui5" → "routing": config (router class, view type, path, controlId, controlAggregation), routes and targets. Fiori tools generators create this for you; you add routes as the app grows.

━━━━━━━━━━━━━━━━━━━━
▶ 3. INITIALIZE AND NAVIGATE

  • Component.js: this.getRouter().initialize() in init.
  • In the list controller: this.getOwnerComponent().getRouter().navTo("detail", { requestId: sId }).
  • Encode keys that contain special characters (encodeURIComponent).

━━━━━━━━━━━━━━━━━━━━
▶ 4. READ PARAMETERS IN THE TARGET VIEW

In the detail controller onInit: router.getRoute("detail").attachPatternMatched(this._onMatched, this). In _onMatched, read oEvent.getParameter("arguments").requestId and bind the view (bindElement) to the entity path. This runs every time the route is hit — onInit runs only once.

━━━━━━━━━━━━━━━━━━━━
▶ 5. FRAGMENTS

A fragment is an XML snippet without its own controller (e.g. a reusable form or a dialog). Load it with Fragment.load({ id, name, controller: this }), add it as a dependent of the view (so it gets models and is destroyed with the view) and open it.

━━━━━━━━━━━━━━━━━━━━
▶ 6. DIALOGS

  • Load the dialog fragment once, keep the promise, reuse it.
  • Close with a button handler; don't forget to reset input.
  • For simple confirmations use sap.m.MessageBox.

━━━━━━━━━━━━━━━━━━━━
▶ 7. NOT FOUND AND BACK

  • Configure a "notFound" target for unknown hashes.
  • Use the router's navTo with bReplace for redirects; browser back works automatically with hashes.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Add the "detail" route and target (code example).
  2. In the list, navigate on item press.
  3. In the detail, bind in _onMatched.
  4. Add a "Reject" dialog fragment and open it from a button.

✅ CHECKPOINT: your app has list → detail navigation and a reusable dialog.

Code example

ABAP Code
// ===== Step 1: manifest.json (sap.ui5 > routing) ====="routing": {  "config": {    "routerClass": "sap.m.routing.Router",    "type": "View", "viewType": "XML",    "path": "zpurchreq.view",    "controlId": "app", "controlAggregation": "pages",    "bypassed": { "target": "notFound" }  },  "routes": [    { "name": "list",   "pattern": "",                      "target": "list" },    { "name": "detail", "pattern": "requests/{requestId}",  "target": "detail" }  ],  "targets": {    "list":     { "name": "List",     "id": "list" },    "detail":   { "name": "Detail",   "id": "detail" },    "notFound": { "name": "NotFound", "id": "notFound" }  }} // ===== Step 2: List.controller.js - navigate on press =====onPress: function (oEvent) {  const oCtx = oEvent.getSource().getBindingContext();  this.getOwnerComponent().getRouter().navTo("detail", {    requestId: encodeURIComponent(oCtx.getProperty("RequestUUID"))  });}, // ===== Step 3: Detail.controller.js - bind on every match =====onInit: function () {  this.getOwnerComponent().getRouter()    .getRoute("detail").attachPatternMatched(this._onMatched, this);},_onMatched: function (oEvent) {  const sId = decodeURIComponent(oEvent.getParameter("arguments").requestId);  this.getView().bindElement({    path: "/PurchaseRequest(RequestUUID=" + sId + ",IsActiveEntity=true)",    parameters: { $select: "RequestID,Description,Status,TotalAmount,CurrencyCode" }  });}, // ===== Step 4: open a dialog fragment (load once, reuse) =====onOpenReject: function () {  if (!this._pRejectDialog) {    this._pRejectDialog = sap.ui.core.Fragment.load({      id: this.getView().getId(),      name: "zpurchreq.view.RejectDialog",      controller: this    }).then(oDialog => { this.getView().addDependent(oDialog); return oDialog; });  }  this._pRejectDialog.then(oDialog => oDialog.open());},onCloseReject: function () {  this.byId("rejectDialog").close();} <!-- ===== view/RejectDialog.fragment.xml ===== --><core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">  <Dialog id="rejectDialog" title="Reject Purchase Request">    <TextArea id="rejectReason" width="100%" placeholder="Reason" />    <beginButton><Button text="Reject" type="Reject" press=".onConfirmReject" /></beginButton>    <endButton><Button text="Cancel" press=".onCloseReject" /></endButton>  </Dialog></core:FragmentDefinition>

Real project scenario

A freestyle app created detail views in controller code and passed the selected object through a global variable. Refreshing the browser on the detail page produced an empty screen, and bookmarks did not work.
The team moved navigation to manifest routing with a "requests/{requestId}" pattern and bound the detail view in attachPatternMatched. Refresh, bookmarks and browser back worked immediately, and a reject dialog was turned into a fragment reused on both the list and detail pages.

Common mistakes

  • Creating views manually instead of using the router.
  • Binding the detail view in onInit (runs only once).
  • Passing data through global variables instead of URL parameters.
  • Loading dialog fragments on every click.
  • Not adding fragments as dependents (models missing, memory leaks).
  • Unencoded keys in route parameters.

Best practices

  • Configure all navigation in manifest.json.
  • Put only keys in the URL; load data in the target view.
  • Bind in attachPatternMatched.
  • Load fragments once, add as dependent, reuse.
  • Configure a notFound target.

Interview angle

Questions: "How does routing work in SAPUI5?", "How do you pass parameters between views?", "What is a fragment?"
Strong answer: routes and targets are configured in manifest.json and the router is initialized in the component; navTo with parameters builds the URL hash; the target controller reads arguments in attachPatternMatched and binds the view; fragments are controller-less reusable UI loaded with Fragment.load, added as dependents; dialogs are typically fragments loaded once and reused.

Practice this lesson

Quick check

1. Where is the detail view of a routed app best bound to its entity?

2. What should be passed as a route parameter when navigating from list to detail?

3. Which dialog handling is inefficient?

Interview questions

1. How do routing and navigation work in SAPUI5, and where do you bind the detail view?

Answer it in your own words first, then compare.

2. After refreshing the browser on a detail page, the page is empty. What is the cause and fix?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics