Quantcast
Channel: SAPUI5 Developer Center
Viewing all articles
Browse latest Browse all 789

Nested UI Routing in OpenUI5

$
0
0

Hi all,

 

I’m a big fan of the SAP javascript framework called OpenUI5. It’s a big change when you come from WDA but it has a lot more advantages. Besides SAP, there are other Javascript frameworks like AngularJS, BackboneJS, EmberJS, DurandalJS, … . Javascript frameworks are a growing trend in the web/mobile technology.

 

The idea

I’ve tested a lot of these frameworks. Most of the time I enjoyed working with OpenUI5 of course. One of the AngularJS plugins that I really like is the Angular UI-Router: https://github.com/angular-ui/ui-router

 

This plugin has a feature which can change multiple components in the view while navigating as you can see here: https://github.com/angular-ui/ui-router#multiple--named-views

 

The concept

For example, when surfing to your Javascripte website with the URL pattern #/home, different subviews/components will be loaded in different places (header, menu, content and footer). When you for example navigate to the “Company” page, you will see that the page stays the same. Only the different subviews/components will change.

 

pic1.png

 

This is in my opinion a really nice feature! Navigate and change multiple components. Some of the advantages:

  • Reusable components/views
  • One master page with the design
  • Separation of code logic

 

Standard OpenUI5

I thought this could be a nice feature for OpenUI5. Wouldn’t it be nice if you could navigate in OpenUI5 without changing the whole page but only some parts of it? Yes, indeed. I’ve been looking at the OpenUI5 SDK and I’ve found an OpenUI5 Router component .

 

Do you want to take a look? Open UI5 SDK: resources\sap\ui\core\routing.

 

That’s exactly what I was looking for! Of course this component was not working exactly the same as the AngularJS UI Router, so I did some changes.

 

I started analyzing the standard OpenUI5 Router component and found out following issues, to have the same feature as in AngularJS Routing you will need to change the following:

 

  • Only one view definition for an URL pattern possible
  • You can use an URL pattern only once
  • Views have to be created in a parent OpenUI5 component

 

So in every view you will have to add the required content when maybe you want some parts from the previous page…

 

pic2.png

An example of how to use a default OpenUI5 Router:

 

var shell = new sap.ui.ux3.Shell("shell", {  appTitle:'SAPUI5 Routing Example',  showLogout: false,  showLogoutButton: false,  showSearchTool: false,  showInspectorTool: false,  showFeederTool: false,
});
shell.placeAt('content');
var oRouter = new sap.ui.core.routing.Router([{  pattern: "",  name: "_main",  view: "customsapui5.main",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "shell",  targetAggregation: "content",  clearTarget: true
},
{  pattern: "secondPage",  name: "_secondPage",  view: "customsapui5.secondView",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "shell",  targetAggregation: "content",  clearTarget: true
}]);
oRouter.register("appRouter");
oRouter.initialize();

You have to define a parent OpenUI5 component where the router component will place the views. This can be for example the shell component:

 

https://openui5.hana.ondemand.com/#test-resources/sap/ui/ux3/demokit/Shell.html

 

Or you could use layout components like HorizontalLayout:

 

https://openui5.hana.ondemand.com/#test-resources/sap/ui/layout/demokit/HorizontalLayout.html

 

Customizing OpenUI5

So what if you want to place your views directly in a DIV container? What if you want to use multiple DIV containers that have to change when you navigate to another page? Why the shell or another parent component?

 

The Shell component of OpenUI5 is nice, but I like to have some more freedom for creativity.

 

I’ve started by taking a copy of the OpenUI5 Router component which you can find under the folder:

 

resources\sap\ui\core\routing --> file: Router-dbg.js

 

In my OpenUI5 project I’ve created a folder “components”. I placed a copy of the “Router-dbg.js” in it and renamed it to “RouterCustom.js”.

 

With just one easy change I already had achieved my first goal, I just added changed the parameter “greedy” from “false” to with the value “true” and I was able to navigate multiple views:

 

pic3.png

The this._oRouter object is an instance of the thirdparty library crossroads. By adding the parameter greedy with value "true", it will function differently. By default this value is false in the crossroads object:

OpenUI5 SDK: resources\sap\ui\thirdparty --> crossroads-dbg.js

greedy.png

 

By using two DIV’s and a VerticalLayout Component for every DIV as parent I could use multiple parents. With following code I could change multiple components while navigating

 

var oLayout = new sap.ui.layout.VerticalLayout("oLayout");
oLayout.placeAt('header');
var oLayout2 = new sap.ui.layout.VerticalLayout("oLayout2");
oLayout2.placeAt('content');
var oRouter = new sap.ui.core.routing.Router([{  pattern: "",  name: "_header1",  view: "customsapui5.header1",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "oLayout",  targetAggregation: "header",  clearTarget: true
},{  pattern: "",  name: "_content1",  view: "customsapui5.content1",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "shell",  targetAggregation: "content",  clearTarget: true
},
{  pattern: "secondPage",  name: "_header2",  view: "customsapui5.header2",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "oLayout",  targetAggregation: "content",  clearTarget: true
},
{  pattern: "secondPage",  name: "_content2",  view: "customsapui5.content2",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "oLayout2",  targetAggregation: "content",  clearTarget: true
}]);

This is going good but still not exactly what I want. It would be better when you just have to define one pattern for multiple views and to add views directly to a DIV without the parent OpenUI5 component!


While looking deeper in the Router component I came in the “sap.ui.core.routing.Route” component.  Because this component changes the views I definitely needed this one. Took a copy of it and placed it in my OpenUI5 project under the folder components.

 

resources\sap\ui\core\routing --> file: Route-dbg.js

 

I also renamed it to “RouteCustom.js”

 

pic4.png

 

After some research I came to the conclusion that the most important part for the navigation is  in the method: “_routeMatched”

 

In this method I’ve added following code:

 

if(oConfig["views"]){  $.each(oConfig.views,function(key,value){  var sViewName = value.view;  if (value.viewPath) {  sViewName = value.viewPath + "." + sViewName;  }  oView = oRouter.getView(sViewName, value.viewType);  if(oView && oView["getController"] && oView.getController()["onBeforeShow"]){  oView.getController().onBeforeShow();  }  oView.placeAt(value.div,"only");  });
}else

 

With this code I check if there are multiple views for one pattern. If that’s the case I do the following:

  1. loop over the views
  2. get the view name
  3. get an instance of the view by using the Router object
  4. If the view has a method “onBeforeShow” go to that method
    1. This is something I have implemented to do some manipulations before showing the view. This could be handy in some cases, but is not required!
  5. Place the view in the div which you can now add to the configuration of the pattern
    1. The OpenUI5 standard logic of the Route component uses the following statement for placing the div: “oTargetControl[oAggregationInfo._sMutator](oView);” . With this statement you always needed a parent OpenUI5 component. In my code this is replaced by “oView.placeAt(value.div,"only");” so we just need a DIV as parent.

 

In the “RouterCustom.js” I’ve changed the reference to my custom route component.

 

pic5.png

 

Now I could use the Router just the way I wanted

 

var oRouter = new sap.ui.core.routing.Router([{  pattern: "",  name: "_main",  views[{  view: "customsapui5.header1",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"header"  },{  view: "customsapui5.content1",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"content"  }]
},
{  pattern: "secondPage",  name: "_secondPage",  views:[{  view: "customsapui5.header2",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"header"  },  {  view: "customsapui5.content2",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"content"  }]
});

In my customized router component I can add multiple views to one pattern!

 

The full package

To get a better picture of how it works and what’s the benefits of this changes are, here an example of how to use.

 

In my example, I’ve created six views:

pic6.png

One view will be used for the navigation which always will be on the page. All the others I will use for content of the page.

 

I will create two pages:

 

First page will exist out of following views/components:

  • Header widget: TitleWidget
  • Left content: GeneralWidget
  • Right content: main

 

On the second page I will add the following views/components:

  • Header widget: TitleWidget
    • For the title we want to keep the same content as on the first page.
  • Left content: WeatherWidget
  • Right content: WeatherContent

 

To do this I coded the following in the index.html:

 

<script>  jQuery.sap.registerModulePath("sap.ui.core.samples.routing", "./");     jQuery.sap.registerModulePath('sap.custom.routing.Router', 'components/RouterCustom/');  jQuery.sap.require("sap.custom.routing.Router");
sap.ui.localResources("customsapui5");
var oRouter = new sap.ui.core.routing.Router([{  pattern: "",  name: "_main",  views:[{  view: "customsapui5.main",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"content"  },  {  view: "customsapui5.GeneralWidget",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"contentWidget"  },  {  view: "customsapui5.TitleWidget",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"headerWidget"  }]  }  ,  {  pattern: "weather",  name: "_weather",  views:[{  view: "customsapui5.WeatherContent",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"content"  },  {  view: "customsapui5.WeatherWidget",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"contentWidget"  },  {  view: "customsapui5.TitleWidget",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"headerWidget"  }]  }  ]);
oRouter.register("appRouter");  oRouter.initialize();
var oModel = new sap.ui.model.json.JSONModel({ title: "Nested UI Routing in SAPUI5" });  sap.ui.getCore().setModel(oModel);  jQuery(function() {sap.ui.template();});  var view = sap.ui.view({id:"idmenu1", viewName:"customsapui5.menu", type:sap.ui.core.mvc.ViewType.HTML});  view.placeAt("menu");</script>

In this code I start loading my customized components, create a router object , set title in my model and add the menu to the menu DIV.

 

In the index.html I also created my layout with the required DIV’s  for navigation:

<body class="sapUiBody" role="application">  <div id="overview" class="center">       <div id="header">            <div id="headerImage" class="left"><img src="images/sapui5.png"></div>            <div id="subheader" class="right">                 <div id="headerTitle" data-type="text/x-handlebars-template"><h1>{{text path="/title"}}</h1></div>                 <div id="headerWidget" ></div>            </div>       </div>       <div id="menu"></div>       <div id="main">            <div id="contentWidget" class="left"></div>            <div id="content" class="right"></div>       </div>  </div>  </body>

The navigation itself is being handled in the controller of the menu view. With the name of the route, which is defined in the router object, it will navigate to the second page and change all the different parts (as defined in the router object).

 

goToPage: function(oEvent){  var oRouter = sap.ui.core.routing.Router.getRouter("appRouter");  oRouter.navTo(oEvent.getParameter("item").getKey());  }

 

Because I use the "NavigationBar" OpenUI5 component for the menu and I use the same name for a route as for the key of "NavigationItem", the key is in the event.


Menu HTML View:

<div data-sap-ui-type="sap.ui.ux3.NavigationBar" data-select="goToPage">  <div data-sap-ui-type="sap.ui.ux3.NavigationItem" data-key="_main" data-text="User information" ></div>  <div data-sap-ui-type="sap.ui.ux3.NavigationItem" data-key="_weather" data-text="Weather information" ></div>  </div>


Result

For the layout I also added some basic CSS..

 

All the views contain some dummy content just for showing you the principal. In the view “main” I implemented the “onBeforeShow” method, just for showing.

 

pic7.png

User information page

pic8.png

Weather information page

pic9.png

The title on the index page and the titleWidget are both connected to the same model. Changing the title in the titleWidget will also change the title in the index page.

 

 

It looks like it’s a new page but actually there are just three components changed.

 

pic10.png

You can find the full example on github: lemaiwo/Nested-UI-Routing-OpenUI5 · GitHub

 

I also added the index and custom components of the router to the attachements

 

When working with this concept, you are able to do some CSS on the master page (index.html). Therefore you could use other UI frameworks as defined in the following blog:

http://scn.sap.com/community/developer-center/front-end/blog/2014/02/18/openui5-with-bootstrap-for-the-design

 

Hope it's useful!

 

Kind regards,

Wouter


Viewing all articles
Browse latest Browse all 789

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>