ui5-typescript-walkthrough

Step 30: Routing and Navigation

So far, we have put all app content on one single page. As we add more and more features, we want to split the content and put it on separate pages.

In this step, we will use the OpenUI5 navigation features to load and show a separate detail page that we can later use to display details for an invoice. In the previous steps, we defined the page directly in the app view so that it is displayed when the app is loaded. We will now use the OpenUI5 router class to load the pages and update the URL for us automatically. We specify a routing configuration for our app and create a separate view for each page of the app, then we connect the views by triggering navigation events.

 


Preview

A second page is added to display the invoice

You can access the live preview by clicking on this link: 🔗 Live Preview of Step 30.

To download the solution for this step as a zip file, just choose the link here: 📥 Download Solution for Step 30.


Coding

webapp/i18n/i18n.properties

First, we add a new text value pair to our resource bundle to define a title for the new detail page we plan to create.


# Invoice List
invoiceListTitle=Invoices
invoiceStatusA=New
invoiceStatusB=In Progress
invoiceStatusC=Done

# Detail Page
detailPageTitle=UI5 TypeScript Walkthrough - Details

webapp/view/Detail.view.xml (New)

Now we add the new Detail.view.xml file to our view folder. Beside of the the root node of the XML structure and the required namespaces, it only contains a Page control that displays the title we just defined in our resource boundle and an ObjectHeader control with a static text Invoice assigned to the title attribute (this we will change in the next step).

<mvc:View
	xmlns="sap.m"
	xmlns:mvc="sap.ui.core.mvc">
	<Page
		title="{i18n>detailPageTitle}">
		<ObjectHeader
			title="Invoice"/>
	</Page>
</mvc:View>

webapp/view/Overview.view.xml (New)

Next, we create another view in the view folder, called Overview.view.xml. We add the root node of the XML structure including the required namespaces to it. Then we copy and paste from the app view everything between and including the Page control to our new view.

For simplicity, we reuse the controller ui5.walkthrough.controller.App for our new view as it only contains our helper method to open the dialog.

<mvc:View
    controllerName="ui5.walkthrough.controller.App"
    xmlns="sap.m"
    xmlns:mvc="sap.ui.core.mvc"
    displayBlock="true">
    <Page title="{i18n>homePageTitle}">
        <content>
            <mvc:XMLView viewName="ui5.walkthrough.view.HelloPanel" />
            <mvc:XMLView viewName="ui5.walkthrough.view.InvoiceList" />
        </content>
    </Page>
</mvc:View>

As we reuse the controller ui5.walkthrough.controller.App for two different views (for the new overview and for the app view), two instances of that controller are instantiated at runtime. In general, one instance of a controller is instantiated for each view that references the controller.


webapp/view/App.view.xml

In the app view, we now remove everything and between the control aggregation pages in the app view as this found its new home in the overview view we just created. We provide an id to the app control, as we want to use this control for our router configuration in the app descriptor in the next step.

<mvc:View
    controllerName="ui5.walkthrough.controller.App"
    xmlns="sap.m"
    xmlns:mvc="sap.ui.core.mvc"
    displayBlock="true">
    <Shell>
        <App
            class="myAppDemoWT"
            id="app"/>
    </Shell>
</mvc:View>

webapp/manifest.json

We thus have everything we need to define a routing from the starting view to the details view we just defined. We want to start with the app view loading our new overview view by default and being replaced by the detail view when a specific route has been hit.

We add a new “routing” section to the sap.ui5 part of the descriptor. There are three subsections that define the routing and navigation structure of the app:

{
    ...
    "sap.ui5": {
        ...
        "resources": {
            "css": [
              {
                "uri": "css/style.css"
              }
            ]
        },          
        "routing": {
          "config": {
            "routerClass": "sap.m.routing.Router",
            "type": "View",
            "viewType": "XML",
            "path": "ui5.walkthrough.view",
            "controlId": "app",
            "controlAggregation": "pages"
          },
          "routes": [
            {
              "pattern": "",
              "name": "overview",
              "target": "overview"
            },
            {
              "pattern": "detail",
              "name": "detail",
              "target": "detail"
            }
          ],
          "targets": {
            "overview": {
              "id": "overview",
              "name": "Overview"
            },
            "detail": {
              "id": "detail",
              "name": "Detail"
            }
          }
        }
    }
}

The router will automatically add the view that corresponds to the current URL into the app control. The router identifies the app control with the ID that corresponds to the property controlId: “app” in the AppDescriptor.

The overview view is always shown when the hash is empty. The detail view is shown when the hash matches the pattern detail.

📌 Important:
The sequence of the routes in the routes definition is important. As soon as a pattern is matched, the following patterns are ignored. To prevent this for a specific route, you use the greedy parameter. If set to true, the route is always taken into account.


webapp/Component.ts

In the component initialization method, we now add a call to initialize the router.

import UIComponent from "sap/ui/core/UIComponent";
import JSONModel from "sap/ui/model/json/JSONModel";

/**
 * @namespace ui5.walkthrough
 */
export default class Component extends UIComponent {
    public static metadata = {
        "interfaces": ["sap.ui.core.IAsyncContentCreation"],
        "manifest": "json"
    };
    init(): void {
        // call the init function of the parent
        super.init();
        
        // set data model
        const data = {
            recipient: {
                name: "World"
            }
        };
        const model = new JSONModel(data);
        this.setModel(model);

        // create the views based on the url/hash
        this.getRouter().initialize();
    };
};

We do not need to instantiate the router manually, it is automatically instantiated based on our configuration in the app descriptor and assigned to the component.

Initializing the router will evaluate the current URL and load the corresponding view automatically. This is done with the help of the routes and targets that have been configured in the manifest.json. If a route has been hit, the view of its corresponding target is loaded and displayed.


webapp/controller/InvoiceList.controller.ts

What is still missing is the event handler that performs a navigation to the detail page by clicking an item in the invoice list: To access the router instance for our app use the static method getRouterFor() on the UIComponent module. On the router we call the navTo method passing the pattern name we defined in our app descriptor for routing to the details page.

import Controller from "sap/ui/core/mvc/Controller";
import JSONModel from "sap/ui/model/json/JSONModel";
import { SearchField$SearchEvent } from "sap/m/SearchField";
import Filter from "sap/ui/model/Filter";
import FilterOperator from "sap/ui/model/FilterOperator";
import ListBinding from "sap/ui/model/ListBinding";
import UIComponent from "sap/ui/core/UIComponent";

/**
 * @namespace ui5.walkthrough.controller
 */
export default class App extends Controller {
		

    onPress(): void {
        const router = UIComponent.getRouterFor(this);
        router.navTo("detail");
    }    
};

webapp/view/InvoiceList.view.xml

In the invoice list view we finally add the press event to the list item we just defined in the controller and set the item type to Navigation so that the item can actually be clicked.

<mvc:View
    controllerName="ui5.walkthrough.controller.InvoiceList"
    xmlns="sap.m"
    xmlns:mvc="sap.ui.core.mvc">
    ...
        <items>
            <ObjectListItem
                title="{invoice>Quantity} x {invoice>ProductName}"
                number="{
                    parts: [
                        'invoice>ExtendedPrice',
                        'view>/currency'
                    ],
                    type: 'sap.ui.model.type.Currency',
                    formatOptions: {
                        showMeasure: false
                    }
                }"
                numberUnit="{view>/currency}"
                numberState="{= ${invoice>ExtendedPrice} > 50 ? 'Error' : 'Success' }"
                type="Navigation"
                press=".onPress">
                <firstStatus>
                    <ObjectStatus
                        text="{
                            path: 'invoice>Status',
                            formatter: '.formatter.statusText'
                        }"/>
                </firstStatus>
            </ObjectListItem>
        </items>
    </List>
</mvc:View>

If you now open the app, you should now see the detail page when clicking an item in the list of invoices.


Conventions

 


Next: Step 31: Routing and Navigation

Previous: Step 29: Debugging Tools


Related Information

Routing Configuration

Methods and Events for Navigation

Initializing and Accessing a Routing Instance

Tutorial: Navigation and Routing

API Reference: sap.m.routing.Router

Samples: sap.m.routing.Router