Navigation and Routing in a glance
Navigation
is one of the most important functionality in any application & UI5 isn’t
any exception either. There are a number of ways to implement navigation in
SAPUI5 for e.g. Shell, EventBus, and Routing. Router which is a sub-class of
sap.ui routing library is the recommended way to implement navigation. In this
article, you will learn how to use SAP UX Shell that uses navigation with
Router instead of using Shell capabilities so replace content at runtime. In
this article – SAPUI5 Navigation with Router, you will learn to implement
routing in SAPUI5 application, set custom URLs for views or pages, and
understand the recommended way of navigation in SAPUI5.
One of the
major advantages of using router is its capability of enabling custom URL for
each view or page in UI5 application. That means, you can access UI5
application with custom URL and share URL to any view or page form the
application. That said, another user does not have to run the application from
scratch. With UI5 router, dynamic URLs offer significant improvement in user
experience.
SAPUI5
Router Project Structure
In this article : SAPUI5: Navigation
with Routing, we will use create two views and perform navigation between these
views with router. We will define & register router in index HTML for the
sake of simplicity. However, it is recommended to define router in a
controller.
Step 1: Create SAPUI5 application project with initial view name
as – View1, and name the project as “routerdemo”
Step 2: Create a new view in the “routerdemo” project, and name
the view as – View2
Create Content for
both Views
We will complete easy part first. Use
following piece of code to create content for both views. We do not have to
create any flashy UI in these views. We will simply create text view and add
static content.
Copy and paste following code in View
1.view.js file. Place the code inside “createContent” function.
var oText = new sap.ui.commons.TextView({text: "This is View1"
})
return oText;
Similarly, copy and paste following
code in View2.view.js file. Place the code inside “createContent” function.
var oText = new sap.ui.commons.TextView({text: "This is View2"
})
return oText;
Create UX Shell, and Router
in Index HTML
For UX Shell to function properly
“sap.ui.ux3” library is required. Add a reference to this library in
data-sap-ui-libs.
Define Shell
Shell is a container that hosts a
number of views together. However, all view rendering is mutually exclusive and
one view is not aware if another view exists. In sum, Shell only knows which
view to load and thus, Shell cannot provide custom URLs for each view or page
it that shell holds.
var oShell = sap.ui.ux3.Shell("shellId",{appTitle: "routerdemo",
showLogoutButton: false,
showSearchTool: false,
worksetItems: [
new sap.ui.ux3.NavigationItem("View1Id",{
key: "View1",
text: "View1"
}),
new
sap.ui.ux3.NavigationItem("View2Id", {
key: "View2",
text: "View2"
})
]}
Above piece of code declares a shell
and defines navigation items inside shell. We have added both View1 and View2
as navigation items inside shell. Remember; always assign ID for each
navigation item so that router can know which view to load when router is
triggered. “View1Id” and “View2Id” are both IDs for navigation items.
Define Initial
Content of Shell
content: [new sap.ui.view({id: "view1Id",
viewName: "routerdemo.View1",
type: sap.ui.core.mvc.ViewType.JS
})]
Above piece of code add default content
that shell should load. Here we have declared that navigation item view1Id
should be initial content. However, you can choose not to define initial
content as router is taking care of the default content. So you can opt to skip
this step and routing will still work without any problem.
Place Shell on the
Page
Now, simply place the shell at
“container” DIV of the index HTML
oShell.placeAt('container');
Define Router
var router = new sap.ui.core.routing.Router([{
pattern: "",
name: "View1",
view: "routerdemo.View1",
viewType: sap.ui.core.mvc.ViewType.JS,
targetControl: "shellId",
targetAggregation: "content",
clearTarget: true,
callback: function() {
oShell.setSelectedWorksetItem("View1Id");
}
},
{
pattern: ["custom", "View2"],
name: "View2",
view: "routerdemo.View2",
viewType: sap.ui.core.mvc.ViewType.JS,
targetControl: "shellId",
targetAggregation: "content",
clearTarget: true,
callback: function() {
oShell.setSelectedWorksetItem("View2Id");
}
}
]);
Defining router has two steps. One, defining the router generic implementation, and two, defining router properties.
- Pattern: [“<url pattern>”, “<view or page id>”]
- Name: Tells router which view to load when given URL pattern is found in the URL
- View: Provide View or Page ID
- View Type: Mention what view type is for the view to be loaded
- targetControl: Specify shell ID which is the user control point or user interaction point
- targetAggregation: Content to be loaded when router navigates to another view
- clearTarget: Clears target when navigation triggers
- callback: Defines a function that places view defined in the router properties and sets that in the selected work item of the shell
Define
HashChanger
Hash Changer
is a library that sets custom URL based on the patter property of the router
and enables application to be accessed by custom URL instead of running
application from the start. The advantage is you can pass on the custom URL to
someone who wants to see only specific page in the application. With hash
changer, router can recognize the URL pattern and loads specific page when
another user runs the application with custom URL.
Copy and
paste below code to define hash changer.
worksetItemSelected: function(e) {this.removeAllContent();
var selected = e.getParameter("key");
var oHashChanger = new sap.ui.core.routing.HashChanger();
oHashChanger.setHash(router.getURL(selected));
}
Register
& Initialize Router
router.register("routerID");
router.initialize();
In the end,
router must be registered so that it can be referred with router ID globally. Also,
initialize the router so that it can be used in the current application.
Note: A reference to hash changer library is
required. Copy and paste following code immediately before you define Shell
Element in Index HTML. jQuery.sap.require("sap.ui.core.routing.Router");
jQuery.sap.require("sap.ui.core.routing.HashChanger");
We need reference to routing &
HashChanger both.
Example of the whole Application
Index HTML<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>
<script src="resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-libs="sap.ui.commons,sap.ui.ux3"
data-sap-ui-theme="sap_bluecrystal">
</script>
<!-- add sap.ui.table,sap.ui.ux3 and/or other libraries to 'data-sap-ui-libs' if required -->
<script>
sap.ui.localResources("routerdemo");
jQuery.sap.require("sap.ui.core.routing.Router");
jQuery.sap.require("sap.ui.core.routing.HashChanger");
var oShell = sap.ui.ux3.Shell("shellId",{
appTitle: "routerdemo",
showLogoutButton: false,
showSearchTool: false,
worksetItems: [
new sap.ui.ux3.NavigationItem("View1Id",{
key: "View1",
text: "View1"
}),
new sap.ui.ux3.NavigationItem("View2Id", {
key: "View2",
text: "View2"
})
],
worksetItemSelected: function(e) {
this.removeAllContent();
var selected = e.getParameter("key");
var oHashChanger = new sap.ui.core.routing.HashChanger();
oHashChanger.setHash(router.getURL(selected));
},
content: [new sap.ui.view({
id: "view1Id",
viewName: "routerdemo.View1",
type: sap.ui.core.mvc.ViewType.JS
})]
});
oShell.placeAt('container');
var router = new sap.ui.core.routing.Router([
{
pattern: "",
name: "View1",
view: "routerdemo.View1",
viewType: sap.ui.core.mvc.ViewType.JS,
targetControl: "shellId",
targetAggregation: "content", //content/Page
clearTarget: true,
callback: function() {
oShell.setSelectedWorksetItem("View1Id");
}
},
{
pattern: ["custom", "View2"],
name: "View2",
view: "routerdemo.View2",
viewType: sap.ui.core.mvc.ViewType.JS,
targetControl: "shellId",
targetAggregation: "content", //content/Page
clearTarget: true,
callback: function() {
oShell.setSelectedWorksetItem("View2Id");
}
}
]);
router.register("routerId");
router.initialize();
</script>
</head>
<body class="sapUiBody" role="application">
<div id="container"></div>
</body>
</html>Working Application
On application initial load
On Navigation to View 2
Now, you can pass the URL with #/custom and application will directly load View 2 instead of loading view1 initially.
Index HTML
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>
<script src="resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-libs="sap.ui.commons,sap.ui.ux3"
data-sap-ui-theme="sap_bluecrystal">
</script>
<!-- add sap.ui.table,sap.ui.ux3 and/or other libraries to 'data-sap-ui-libs' if required -->
<script>
sap.ui.localResources("routerdemo");
jQuery.sap.require("sap.ui.core.routing.Router");
jQuery.sap.require("sap.ui.core.routing.HashChanger");
var oShell = sap.ui.ux3.Shell("shellId",{
appTitle: "routerdemo",
showLogoutButton: false,
showSearchTool: false,
worksetItems: [
new sap.ui.ux3.NavigationItem("View1Id",{
key: "View1",
text: "View1"
}),
new sap.ui.ux3.NavigationItem("View2Id", {
key: "View2",
text: "View2"
})
],
worksetItemSelected: function(e) {
this.removeAllContent();
var selected = e.getParameter("key");
var oHashChanger = new sap.ui.core.routing.HashChanger();
oHashChanger.setHash(router.getURL(selected));
},
content: [new sap.ui.view({
id: "view1Id",
viewName: "routerdemo.View1",
type: sap.ui.core.mvc.ViewType.JS
})]
});
oShell.placeAt('container');
var router = new sap.ui.core.routing.Router([
{
pattern: "",
name: "View1",
view: "routerdemo.View1",
viewType: sap.ui.core.mvc.ViewType.JS,
targetControl: "shellId",
targetAggregation: "content", //content/Page
clearTarget: true,
callback: function() {
oShell.setSelectedWorksetItem("View1Id");
}
},
{
pattern: ["custom", "View2"],
name: "View2",
view: "routerdemo.View2",
viewType: sap.ui.core.mvc.ViewType.JS,
targetControl: "shellId",
targetAggregation: "content", //content/Page
clearTarget: true,
callback: function() {
oShell.setSelectedWorksetItem("View2Id");
}
}
]);
router.register("routerId");
router.initialize();
</script>
</head>
<body class="sapUiBody" role="application">
<div id="container"></div>
</body>
</html>Working Application
On application initial load
On Navigation to View 2
Now, you can pass the URL with #/custom and application will directly load View 2 instead of loading view1 initially.
Comments (4)
I hate to disturb you over small stuffs like this.
Is just that I have been learning SAP UI5 for some time now.
I decided to have a different perspective with your tutorials because you brought in the Component.JS and Manifest.Json concepts which are SAP's standard.
But am having a little issue again. I can't Bind data from controllers to Views.
Naturally I should instantiate the Data in the Init functions in the controller and Bind it to the view and it works, but now I guess I have to do some other set up in Manifest.json and Component.js for the data to be binded. and I dont have an idea on how to do that.
Please see Code below.
I have started with SAPUI5 recently, and have been following your tutorial videos. I was following everthing until now, however I have been struggling with Routing between javascript views. I am able to route between XML views, and from XML view to JS view, however I am not able to navigate from JS view to JS view. The first view is loaded, but when routing to the second view, the message on console is "Uncaught TypeError: Cannot read property 'navTo' of undefined".
Please help me out of this.
The code snippets are:
Component.JS
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"sapui5/app53/model/models"
], function(UIComponent, Device, models) {
"use strict";
return UIComponent.extend("sapui5.app53.Component", {
metadata: {
manifest: "json"
},
init: function() {
UIComponent.prototype.init.apply(this, arguments);
this.setModel(models.createDeviceModel(), "device");
this.getRouter().initialize();
}
});
});
Routing part within manifest.json
"routing": {
"config": {
"viewType": "JS",
"viewPath": "sapui5.app53.view",
"targetControl":"idPageContainer",
"targetAggregation": "pages",
"routerClass": "sap.m.routing.Router"
},
"routes": [{
"pattern": "",
"name": "First",
"view": "zjs_view_53_02",
"targetAggregation": "pages"
},
{
"pattern": "second",
"name": "Second",
"view": "zjs_view_53_03",
"targetAggregation": "pages"
}]
}
Container view:
sap.ui.jsview("sapui5.app53.view.zjs_view_53_01", {
getControllerName : function() {
return "sapui5.app53.controller.zjs_view_53_01";
},
createContent : function(oController) {
this.setDisplayBlock(true);
return sap.m.App("idPageContainer");
}
});
First View
sap.ui.jsview("sapui5.app53.view.zjs_view_53_02", {
getControllerName : function() {
return "sapui5.app53.controller.zjs_view_53_02";
},
createContent : function(oController) {
var oButton = new sap.m.Button({
id : "idButton1", // sap.ui.core.ID
text : "Go to Next View", // string
press : [ oController.onNextView ]
});
return new sap.m.Page({
title: "Title Page 2",
content: [
oButton
]
});
}
});
Controller for First View
sap.ui.controller("sapui5.app53.controller.zjs_view_53_02", {
onNextView: function() {
var router;
router = sap.ui.core.UIComponent.getRouterFor(this);
return router.navTo("Second", null);
},
}
var oButton = new sap.m.Button({
id : "idButton1", // sap.ui.core.ID
text : "Go to Next View", // string
press : [ oController.onNextView, oController ]
});