Navigation between views using Routing and parameter passing between views
Hi folks,
This blog mainly explains about navigating and parameter passing between two views.
Below is the Project directory.
Steps to follow
1. Create an SAPUI5 Application Project.
INDEX.HTML
2. Create a JS file named Component.js and place it inside WebContent folder.
(It mainly comprises of four steps :Configuration, Initialization,Access and view ownership)
jQuery.sap.declare("sap.ui.demo.Component");
sap.ui.core.UIComponent.extend("sap.ui.demo.Component", {
metadata : {
routing : {
config : {
viewType : "JS",
viewPath : "routingdemo",
targetControl : "NavContainer",
clearTarget : false,
},
routes : [
{
pattern : "", // which appears in URL, while you navigate
name : "first", // Name that is used in navTo method
view : "FirstPage", // this is the target view that you are navigating to
viewPath : "routingdemo", // path of the target view
targetAggregation : "pages" // this defines whether the target view is a [pages/content/masterpages/detailpages]
},
{
pattern : "InSecondPage",
name : "second",
view : "SecondPage",
viewPath : "routingdemo",
targetAggregation : "pages"
},
]
}
},
init : function () {
// 1. some very generic requires
jQuery.sap.require("sap.m.routing.RouteMatchedHandler");
jQuery.sap.require("sap.ui.demo.MyRouter");
// 2. call overridden init (calls createContent)
sap.ui.core.UIComponent.prototype.init.apply(this, arguments);
// 3a. monkey patch the router
var router = this.getRouter();
router.myNavBack = sap.ui.demo.MyRouter.myNavBack;
// 4. initialize the router
this.routeHandler = new sap.m.routing.RouteMatchedHandler(router);
router.initialize();
},
destroy : function () {
if (this.routeHandler) {
this.routeHandler.destroy();
}
// call overridden destroy
sap.ui.core.UIComponent.prototype.destroy.apply(this, arguments);
},
createContent : function () {
// create root view
var oView = sap.ui.view({
id : "app",
viewName : "routingdemo.App",
type : "JS",
});
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData(
{
myName : null,
myPass : null
}
);
oView.setModel(oModel);
return oView;
}
});
3. MyRouter.js (Custom Router)
jQuery.sap.declare("sap.ui.demo.MyRouter");
sap.ui.demo.MyRouter = {
/* * to monkey patch the router with the mobile nav back handling
*/
myNavBack : function (route, data) {
var history = sap.ui.core.routing.History.getInstance();
var url = this.getURL(route, data);
var direction = history.getDirection(url);
if ("Backwards" === direction) {
window.history.go(-1);
} else {
var replace = true; // otherwise we go backwards with a forward history
this.navTo(route, data, replace);
}
},
};
4. Create an App View .
App.view.js ( In app.view we usually have a sap.m.App or sap.m.SplitApp depending upon the requirement)
sap.ui.jsview("routingdemo.App", {
/** Specifies the Controller belonging to this View.
* In the case that it is not implemented, or that "null" is returned, this View does not have a Controller.
* @memberOf routingdemo.App
*/
getControllerName : function() {
return "routingdemo.App";
},
/** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed.
* Since the Controller is given to this method, its event handlers can be attached right away.
* @memberOf routingdemo.App
*/
createContent : function(oController) {
this.setDisplayBlock(true);
return new sap.m.App("NavContainer");
}
});
4. Create FirstPage View
a) FirstPage.view.js Basic page which accepts two inputs( Name and password) on click of a button, we call a navigate function , which is wriiten in the controller part.
sap.ui.jsview("routingdemo.FirstPage", {
getControllerName : function() {
return "routingdemo.FirstPage";
},
createContent : function(oController) {
var flexbox=new sap.m.FlexBox({direction:"Column"});
flexbox.addItem( new sap.m.Input("name",{placeholder:"Enter UserName" value : "{/myName}"}));
flexbox.addItem( new sap.m.Input("password",{placeholder:"Enter Password" value : "{/myPass}"}));
flexbox.addItem( new sap.m.Button('login',{text:"Log In",
press:function()
{
oController.navigate();
}
})
);
flexbox.setAlignItems("Center");
flexbox.setJustifyContent("Center");
var page1 =new sap.m.Page({
title: "Routing Demo",
content:flexbox,
});
return page1;
}
});
b) FirstPage.Controller.js
(In controller we get the instance of the router in the init method and use it to navigate to the specific page. we pass the parameters between the views by using an object and binding to a modelname. We use navTo method to navigate)
sap.ui.controller("routingdemo.FirstPage", {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
* @memberOf routingdemo.FirstPage
*/
onInit: function()
{
this.router = sap.ui.core.UIComponent.getRouterFor(this);
},
navigate : function()
{
this.router.navTo("second");
}
});
5. Create SecondPage View
a) SecondPage.View.js ( We can get the object in the model and use it in secondpage view ie object.myname ) object is userdefined.
sap.ui.jsview("routingdemo.SecondPage", {
getControllerName : function() {
return "routingdemo.SecondPage";
},
createContent : function(oController) {
var flexbox=new sap.m.FlexBox({direction:"Column"});
flexbox.addItem( new sap.m.Text({text : "{/myName}"}));
flexbox.addItem( new sap.m.Text({text : "{/myPass}"}));
flexbox.setAlignItems("Center");
flexbox.setJustifyContent("Center");
var page2 =new sap.m.Page({
content:flexbox,
showNavButton: true,
navButtonPress: function(){ oController.handleNavBack(); },
});
return page2;
}
});
b) SecondPage.controller.js ( we navigate back by using myNavBack method , which we have defined in our MyRouter.js , which is a custom Router)
sap.ui.controller("routingdemo.SecondPage", {
onInit: function() {
this.router = sap.ui.core.UIComponent.getRouterFor(this);
},
handleNavBack : function(){
this.router.myNavBack("first");
}
});
And the final output .
You can see the change in the URL,(InsecondPage). You can navigate Back by clicking on the back icon.
Hope this Blog helps.
Regards
Indrajith
Hi Indrajith,
Its a good blog; though I am not too clear on the relevance of the concept of 'routing'. I have read routing related blogs, but still not clear on the utility of it.
What i mean is say, if I want to navigate from one page to any other page in my app, I could just say app.to("next_page_id") and that would do it for me. This way, there will not be any change in the app URL either.
Hence, I fail to understand the significance of routing, though I am sure it is something useful. Just not sure when and why so.
Could you brief up why do we use routing when we can navigate across pages without it as well?
Regards,
Rutika Bodas
Hi Rutika,
Thank you for going through the Blog.
Routing is the recommended and most preferred method for SAPUI5 applications. The main advantage of using Routing is that it provides you an ability to bookmark a particular navigation state . However app.to/panel works fine to navigate, It will have an effect on the performance when you have more number of views. You can go through the below SAPUI5 SDK - Demo Kit
to get much idea about routing.
Regards
indrajith
Thanks for the reply.
Hi Indrajith,
It's a good blog. Thanks for sharing it.
Thanks varun.
Although I like your blog -- but could you please reformat the code for better readability? -- it contains one big error...
Upon clicking the navigate button in the first page, you retrieve the input controls' values, store it in a model.
When displaying the second page, you retrieve the model, and set the text control's 'text' property to the value from the model's inner property. This is a real bad pattern, it doesn't use databinding, and as such it's not how models should be used!
To use proper databinding, your code should be more like this:
1) Inside your Component.js, or first page controller, define a global JSON model:
2) In the first page controller, your navigate function should be just this:
3) Now, in the first view, apply the data binding to the input controls:
4) Then, in the second view, you can simply use this:
NB: If your intention was to show how you could pass data between two views without using a shared model, a much better approach would be to use the EventBus, and have the first view publish an event and have the second view subscribe to that event.
Hi Robin,
Thank you for your valuable inputs.
I tried using two data binding before posting the blog itself, but it does'nt seem to work.
So i choose the other one.
The two way data binding seems to work when i don't include the component.js file,where i write the routing configuration in index.html . could you please provide some inputs on the same.
regards
Indrajith
Ok, but the way it is written now is really incorrect, and IMHO no UI5 developer should connect controls like this to a model (I call it connecting since this is not databinding)
For me to comment on that I should see the original code. Having a Component.js and a two-way binding model is how most of the UI5 applications are built, so it should work just fine. Maybe you can post a topic on the forum with the code which isn't working
Hi Indrajith,
I appreciate your efforts in putting in this blog however I completely agree with Robin van het Hof on the bad pattern of moving data using another model.
As suggested by Robin, in your navigate method pass the arguments along with the routes name. Arguments would be part of the pattern.
Also in your SecondPage.controller.js in onInit() method use the route instance(router) method - attachRouteMatched or attachRoutePatternMatched to get the arguments passed in Navigate method of FirstPage. More info on these methods - SAPUI5 SDK - Demo Kit
I think the key for routing and navigation would be Pattern Matching
Hi kedar,
Thank you for going through the blog and sharing your inputs.
You suggested to pass the arguments along with the routes name. But if that is the case, then those parameters will be visible in the URL right? Don't you think there might cause some security issues. And what if i want to fetch 10 values from a textfield and then pass it to the next view. At that time the URL looks clumsy.
Regards
Indrajith
Agreed.
But the ultimate location to launch the application is either the Fiori Launchpad(on the browser) or Fiori Client(on the mobile) you will not see the parameters.
Actually, I was referring to using the EventBus, and not passing arguments via the URL pattern 😉 . I agree with Indrajith this isn't a viable approach for passing more than two arguments.
See the Application Best Practices guide, chapter Routing for more details on how to use the EventBus
But the document says, a new navigation technique is introduced which superseded EventBus:
Per the documentation:
A new Routing mechanism was introduced to UI5 in release 1.16. For in-app navigation, this supersedes previous techniques such as using the
sap.ui.core.EventBus
or sharing navigation-container specific controller code amongst aggregated pages.
And recommends using other techniques for Intra-application navigation:
While these previous techniques work well for intra-application navigation, they don't cater for the requirements for bookmarking and resuming application User Interface (UI) state.
I am missing something?
Well, I'm not using the EventBus for Navigation itself -- which is indeed rightfully deprecated -- but you for passing chunks of data while navigating with a router -- which is still supported, see the Application best Practices
Another way of course is to use a global model, which is available to all views in an application
Hi Robin,
I have updated the blog based on your inputs. I have used Two-way data binding and i works fine..!! Thanks for your valuable inputs.
Regards
Indrajith
Thanks indrajith. It is very helpful.
Hi Indrajith,
If i put ClearTarget as TRUE in the router defination i'e in component.js it is showing some error that
" Unable to get property 'getId' of undefined or null reference ".
Regards,
bhaskar.
ClearTarget : true means it will clear the target aggregation before we add the views. So you should not clear the target aggregation.Set it as false
Thanks for the quick reply Indrajith,
As per the my requirement i have to clear the target and i need to append the view again since view content will change dynamically
Hie Indrajeet,
Can you help me out with following routing issue?
I have written menuBar code inside a fragment, (there is a fragment folder created inside web content, and inside this folder menu.js file contained). Now i call this fragment in one of my views successfully, but when i click on menu item so as to navigate to another view, it gives me following routing error:
cannot read property navTo of undefined.
I have created component.js file exactly like yours, giving routes of my views.
On select event of menu bar itemi call one of the controller functions as follows:
var quickMenuItm1 = new sap.ui.commons.MenuItem({text:"Place Order",
select :function(oEvent) {
sap.ui.controller("demo.dashboard").handleOrder();
},
});
Though function gets called successfully, it does not navigate to another view instead gives me above mentioned error.
Code for the function inside controller:
onInit: function() {
this.router = sap.ui.core.UIComponent.getRouterFor(this);
}
handleOrder:function(evt) {
this.router.navTo("orders");
},
I think its not able to find router, but i do not understand why? without using fragment when i called the same function from view it worked, then why not here?
Can you please help?
Thank you,
Best Regards,
Chetna
Hi chetna, i have replied to your thread.
How to call controller functions from index.html in sapui5.
Thanks Indrajith for the blog. Could you please highlight the parameters of sap.ui.core.UIComponent.getRouterFor(this);
it is very goog, where can i download the example?
It is a very useful tutorial. I am searching for some routing tutorial, get some information from Spotify Customer Service. Anyone have any idea about it. Looking forward to a solution.