In this blog, you will find details about how to build a splitApp mobile application using SAPUI5 and also how to use routing mechanism to navigate between different views. Before reading further please look at the below video, which shows how the final application looks like SAPUI5 splitApp with Routing mechanism - YouTube.Also download the complete project MyRoutingDemo.zip - Google Drive and import into your Eclipse IDE. It helps having the code with you while reading the blog.
I assume that you have basic knowledge of SAPUI5 and developed some basic applications using it in Eclipse. The application has total five JS type views and their corresponding controllers.
-Empty view – No content
-MainView view – This view holds splitApp control
-Years view – This view displays years
-Categories view – This view displays difference categories like Movie, Actor etc.,
-NomandWinner view – This view displays nominees and winner for a particular category
I have created a JSON file which holds model data. While I am talking about JSON model, I have to mention about website http://www.jsoneditoronline.org/ . I have used this website to create my JSON model, this has very simple editor and you can see JSON file in terms of individual objects in it.
Let's look at the JSON model data.json file
Here I have given a snapshot of one element of the model
'oscars' node has 5 objects, each one in turn has 2 objects one of them is categories object, which has again 3 objects, where 'nominee' is one of them. Nominee object in turn has 3 objects, where each of them has single element called 'nominee'. ( I know how you feel after reading this explanation sorry for that )
Let’s say I want to read different categories for year '2010', then I have to go to ‘0’ element of array ‘oscars’ and then access ‘categories’ element and then access ‘category’ element. In terms of path definition this can be represented as ‘oscars/0/categories’
If I want to read different nominee for a ‘movie’ category in year 2010 then it will be ‘/oscars/0/categories/0/nominees’
Let’s look at different files in the project I have written comments for each line in the code so I am not going to explain them here again.
index.html
Router declaration and initialization happens in index.html file.
<!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.m,sap.ui.commons" data-sap-ui-theme="sap_bluecrystal"> </script> <!-- only load the mobile lib "sap.m" and the "sap_bluecrystal" theme --> <script> sap.ui.localResources("Views"); //Folder where views JS files are stored jQuery.sap.require("sap.ui.core.routing.Router"); // load Router module jQuery.sap.require("sap.m.routing.RouteMatchedHandler"); // load RouteMatchedHandler module //Initilaize the MainView var app = sap.ui.view({id:"idMainView1", viewName:"Views.MainView", type:sap.ui.core.mvc.ViewType.JS}); //Place the MainView content in "Content" Area app.placeAt("content"); // Create an instance of Router class var oRouter = new sap.ui.core.routing.Router( { rcategories: { //Route 'rcategories' takes you from the current view to view 'Categories' pattern: "oscars/{year}", // This pattern is placed after the hash('#') symbol in URL // varaible inside {}, 'year' in this case is replaced with value later // in method itempressed() inside controller of 'Years' view view: "detail.Categories", // This is the target view, to where navigation happens }, rnomwinner: { //Route 'rnomwinner' takes you from the current view to view 'NomWinner' pattern: "nominees/{year}/{cat}", // This pattern is placed after the hash('#') symbol in URL // varaible inside {}, 'year' and 'cat' in this case are replaced with values later // in method itempressed() inside controller of 'Categories' view view: "detail.NomWinner" , // This is the target view, to where navigation happens } }, { targetControl: "splitapp", // targetControl refers to the id of control where all the target views are to be placed // this is defined in 'MainView' createcontent method targetAggregation: "detailPages", // targetAggregation refers to aggregation namw of targetControl. For splitApp // control there are two Aggregations, 'detailPages' and 'masterPages' viewType: "JS", // viewType defines type of the target views viewPath: "Views", // The view path parameter specifies a prefix that prepends the target views. In our case // target view detail.Categories will be takes as Views.detail.Categories. Please note that // view Categories is placed under folder Views/detail/ clearTarget: false //The clear target parameter defines a boolean that is used to specify if the aggregation //should be cleared before adding the view to it. }); var oRouteHandler = new sap.m.routing.RouteMatchedHandler(oRouter); oRouter.register("router"); // Assign a name to Router, so that we can access it in all controllers by using this name oRouter.initialize(); // Initialise the Router </script> </head> <body class="sapUiBody" role="application"> <div id="content"></div> </body></html>
Years View
createContent : function(oController) { // Create a standardlistitem control by binding 'title' property with element 'year' of the JSON model var oItemTemplate = new sap.m.StandardListItem({title : "{year}", type : sap.m.ListType.Active}); // Create 'List' control having 'oscars' node of model as binding to 'items' aggregation and add a // eventhadnler method ( 'itempressed' method in controller) to 'itemPress event. This event gets triggered // whenever user press a Listeitem var oList = new sap.m.List({ items : {path:"/oscars", template:oItemTemplate}, itemPress : function(oEvent){ oController.itempressed(oEvent); } }); // Add List control to a new Page control and return it var oPage = new sap.m.Page({ title: "Years", content: [oList] }); return oPage; } });
Year view Controller
// This method gets triggered whenever a item in the List is pressed. Here we trigger // navTo() method of Router to initiate the navigation itempressed: function(oEvent) { // Get instance of router this.oRouter = sap.ui.core.routing.Router.getRouter("router"); // Get Array if all items in List control var a = oEvent.getSource().getAggregation("items"); // Get reference to current Listitem that has been clicked var b = oEvent.getParameters().listItem; // In order to know the position of the clicked Listitem with in the List // we loop over all items and compare each Listitem with the clicked Listitem for (var i = 0; i < a.length; i++) { if (a[i] == b) { break; // we have a match so exit the loop. At this point 'i' refers to index of clicked Listitem } } // Call navTo() method of Router class to trigger the navigation // For route "rcategories' pattern is defined as 'oscars/{year} where 'year' is a variable // which needs to be repalced by a value. Here we are replacinf it with the index of Listitem // that has been clicked by user this.oRouter.navTo("rcategories", { year : i }); } });
Categories View
createContent : function(oController) { // Create a standardlistitem control with id 'idlistitem' and by binding 'title' property with element // 'category' of the JSON model var oItemTemplate = new sap.m.StandardListItem(this.createId("idlistitem") , {title : "{category}", type : sap.m.ListType.Active}); //Create 'List' control with id 'idlist' and add a //eventhadnler method ( 'itempressed' method in controller) to 'itemPress event. This event gets triggered // whenever user press a Listeitem var oList = new sap.m.List(this.createId("idlist"), { itemPress : function(oEvent){ oController.itempressed(oEvent); } }); //Add List control to a new Page control and return it var oPage = new sap.m.Page({ title: "Categories", content: [oList] }); return oPage; } });
Categories View Controller
onInit: function() { // Get instance of Router this.oRouter = sap.ui.core.routing.Router.getRouter("router"); // Call 'attachRouteMatched method,which defines a function that gets called everytime // a 'RouteMatched' event is triggered by application this.oRouter.attachRouteMatched(function(oEvent) { // get the name of the Route var sRoute = oEvent.getParameter("name"), // Get the name of the target view oView = oEvent.getParameter("view"); // As this method gets called for all routes defined in the application we have to //make sure that code gets executed only for the route that we are interested in if(sRoute == "rcategories") { // Define the binding path var spath = "/oscars/" + oEvent.getParameter('arguments').year + "/categories"; // Bind aggragation 'Items' of List with above defined path pass listitem with id 'idlistitem' as template oView.byId("idlist").bindItems(spath, oView.byId("idlistitem")); } }); }, itempressed: function(oEvent) { //Get the instance of Router this.oRouter = sap.ui.core.routing.Router.getRouter("router"); //Get the instance of HashChanger this.oHasher = sap.ui.core.routing.HashChanger.getInstance(); //Get the current hash value in the URL and split the URL at character '/' // if the hash value is 'oscars/2', then below statemnet will split that into //'oscars' and '2'. we are interested in element with index 1 i.e. second element in array 'j' var j = this.oHasher.getHash().split("/")[1]; // Get all instances of Listitems present in List var a = oEvent.getSource().getAggregation("items"); //Get instance of Listitem which user has clicked var b = oEvent.getParameters().listItem; //To get the index of the Listitem that has been clicked loop over the array of Listitems // and compare with clicked listitem that is 'b' for (var i = 0; i < a.length; i++) { if (a[i] == b) { break; //we have a match so exit the loop } } //navigate to Route 'rnomwinner'. pass values to variables 'year' and 'cat' in patter 'nominees/{year}/{cat}' this.oRouter.navTo("rnomwinner", { year : j , cat : i}); } });
NomWinner view
createContent : function(oController) { // Create a standardlistitem control with id 'idlistitem' and by binding 'nominee' property with element // 'category' of the JSON model var oItemTemplate = new sap.m.StandardListItem(this.createId("idlistitem"), {title : "{nominee}", type : sap.m.ListType.Active}); //Create 'List' control with id 'idlist' var oList = new sap.m.List( this.createId("idlist")); //Create a Button control with id 'idbutton' and add an eventhandler method 'click' of controller // to action 'press' var oButton = new sap.ui.commons.Button(this.createId("idbutton"),{ width : "300px", height : "100px", style: sap.ui.commons.ButtonStyle.Accept, press : function(oEvent) { oController.click(oEvent); } }); //Add List control and Button control to a new Page control and return it this.page = new sap.m.Page({ title: "Nominees and Winner", navButtonText: "Back", showNavButton: true, navButtonPress: [oController.onNavButtonTap, oController], content: [oList, oButton] }); return this.page; } });
NomWinner view Controller
onInit: function() { // Get instance of Router this.oRouter = sap.ui.core.routing.Router.getRouter("router"); // Call 'attachRouteMatched method,which defines a function that gets called everytime // a 'RouteMatched' event is triggered by application this.oRouter.attachRouteMatched(function(oEvent) { // Get the name of the Route var sRoute = oEvent.getParameter("name"), // Get the name of the target view oView = oEvent.getParameter("view"); // As this method gets called for all routes defined in the application we have to //make sure that code gets executed only for the route that we are interested in if(sRoute == "rnomwinner") { // Define the binding path var spath = "/oscars/" + oEvent.getParameter('arguments').year + "/categories/" + oEvent.getParameter('arguments').cat + "/nominees"; // Set the button text back to original value every time user clicks on new category oView.byId("idbutton").setText("And the Winner is...."); // Bind aggragation 'Items' of List with above defined path pass listitem with id 'idlistitem' as template oView.byId("idlist").bindItems(spath, oView.byId("idlistitem")); } }); }, click: function(oEvent){ //Get the instance of HashChanger this.oHasher = sap.ui.core.routing.HashChanger.getInstance(); //Split the hash value at '/'. this will give you an array with individual values var j = this.oHasher.getHash().split("/"); //Get the value of element 'winner' in JSON model at a given path var winner = sap.ui.getCore().getModel().getProperty("/oscars/"+j[1]+"/categories/"+j[2]+ "/winner"); //Set the value of property 'text' of button with value of 'winner' this.getView().byId("idbutton").setText(winner); }, // Navigate previous entry in browser history //This function is called when user clicks on back button in detail view onNavButtonTap : function() { window.history.go(-1); } });
I would like to discuss more about how routing works rather than explaining how the different views are built. Let's divide the routing into different parts and analyse each part in detail.
1 - Definition and initialization of Routes
Routes definition consists of giving a name to the route, name of the target view and pattern value if needed.Now there are three statements that we need understand
sap.m.routing.RouteMatchedHandler(oRouter)– Create an instance of RouteMatchedHandler class. Inside the constructor, two standard methods are attached to two events ‘RouteMatched’ and ‘RoutePatternMatched’. These method handle the placement of the target view based on the configuration parameters ‘targetControl’ and ‘targetAggregation’ if the view needs to be placed in masterPages or ‘detailPages’
oRouter.register("router") - Registers the router to access it from another context. Use sap.ui.routing.Router.getRouter() to receive the instance, bu passing ’router’ as the value.
oRouter.initialize() - Attaches the router to the hash changer. In this method we create instance of sap.ui.core.routing.HashChanger and attach event handler function to event ‘hashChanged’.
2- How navigation happens using routes
In any event handler method inside the controller of source view we should call method "navTo" of class sap.ui.core.routing.Router. In our case to navigate to 'Categories' view we use below code in event handler method 'itempressed' inside controller of view 'Years'
this.oRouter.navTo("rcategories", { year : i });
Here first parameter takes name of the route and second parameter is used to fill in the variables in the 'pattern' property of the specific route. Route rcategories has pattern 'oscars/{year}'. Here variable 'year' is replaced with value 'i' and this pattern is placed after '#' in the URL. When I click on 2011, value after # is changed to 'oscars/1' as shown below.
Similarly if we click on 'Actress', itempressed() method in controller of view 'Categories' is called and there we have the below code
this.oRouter.navTo("rnomwinner", { year : j , cat : i});
If you look at the definition of Route 'rnomwinner' ( nominees/{year}/{cat }), we see two variables 'year' and 'cat'. So in the 'navTo' method we pass values to these two variables. Below is how the URL looks once we click on any of the category
Now let's look at how navTo() method actually works. below is the call stack ( I have added only important methods )
Method | Class |
---|---|
navTo() - Initiates the navigation to target view | sap.ui.core.routing.Router |
setHash - change hash value to new value | sap.ui.core.routing.HashChanger |
fireHashchanged - fire event 'HashChanged' | sap.ui.core.routing.HashChanger |
RouteMatched(Inside this method 'Createcontent()' method of the target view is called) | sap.ui.core.routing.Route |
fireRoutematched - Fire event 'RouteMatched' | sap.ui.core.routing.Router |
attachRouteMatched - In this method we can write custom logic on what to do when hash value matches with a ‘pattern’ value of certain route ( for example, bind a property of control with an element /collection of model. In our case we bind the aggregation ‘items’ of ‘List’ control with ‘categories’ node) | In target view controller ( in our case it is placed in oninit() method of target view controller |
So to summarize navTo() method results in following
- Set the hash value to new value
- Find out all the routes that have pattern value which matches with the new hash value. Once we have match, fire 'routematched' event, which calls all 'attachroutematched' methods
Please note that I am no way an expert on SAP UI5, all that is presented here is done by reading/exploring stuff on my own. If there are any mistakes or blunders that I did, please feel free to comment. I really appreciate that.
Thanks for reading my blog and keep sharing