Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
GrahamRobbo
Active Contributor
*Note Sample code is at https://github.com/Yelcho/UI5-Comp-Routing

I eagerly consumed all the UI5Con presentations as soon as they became available. You can find them at https://www.youtube.com/channel/UCOlLpeus2uAJhmxjKHHGTgA

The presentation I enjoyed the most was from Thorsten Erlewein called "Awesome UI5 Features and why we use them in Fiori elements".



One of the first things Thorsten touched on was Component Based Routing. I had looked at breaking my UI5 apps into smaller components many times over the years - but always found difficulties getting routing between components to work as I would like.

tom.vandoorslaer described the way he handles Component Based Routing in his blog "UI5 – Nested routing with reusable components". Tom highlights the challenges of handling wildcards in routing patterns and I must admit I didn't really want to go there.

I took Thorsten's presentation as a sign that I should try again.

The first thing I did was create three distinct components that display different entities from the Northwind sample oData service. They are Products, Suppliers and Categories and they all work the same way.

They have an App view that consists of a sap.m.App control for supporting navigation. A List view that consists of a sap.m.Page control that contains a sap.m.Table control that renders the appropriate entity set using a sap.m.ColumnListItem control of type sap.m.ListType.Navigation.





When selecting the ColumnListItem control we navigate to the Detail view and bind this view to the chosen entity to display the entity details.





Importantly the navigation concept provided by the sap.m.routing.Router class means we can navigate backwards and forwards using the browser buttons and we can do a browser refresh at any time and be returned to the same view and context.

Now it is time to incorporate these components into a new application. I created an app that has a default view containing a sap.tnt.Toolpage to provide a menu system with an entry for each of the three components built earlier. The ToolPage control contains its' own sap.m.App control which will be used as a navigation container for the components. The components could be located anywhere, including in their own library, but for this example I simply placed them into a "reuse" subdirectory.



I used the SAPUI5 documentation to figure out how to configure component-based routing in my manifest file. The relevant areas looks like this...
{
...
"sap.ui5": {
"rootView": {
"viewName": "yelcho.mydemo.comprouting.view.App",
"type": "XML",
"async": true,
"id": "app"
},
"componentUsages": {
"suppliersComponent": {
"name": "yelcho.reuse.suppliers",
"settings": {},
"componentData": {},
"lazy": true
},
"categoriesComponent": {
"name": "yelcho.reuse.categories",
"settings": {},
"componentData": {},
"lazy": true
},
"productsComponent": {
"name": "yelcho.reuse.products",
"settings": {},
"componentData": {},
"lazy": true
}
},
...
"routing": {
...
"routes": [
{
"name": "home",
"pattern": "",
"target": "home"
},
{
"name": "suppliers",
"pattern": "suppliers",
"target": {
"name": "suppliers",
"prefix": "s"
}
},
{
"name": "categories",
"pattern": "categories",
"target": {
"name": "categories",
"prefix": "c"
}
},
{
"name": "products",
"pattern": "products",
"target": {
"name": "products",
"prefix": "p"
}
}
],
"targets": {
"home": {
"viewId": "home",
"viewName": "Home"
},
"suppliers": {
"type": "Component",
"usage": "suppliersComponent"
},
"categories": {
"type": "Component",
"usage": "categoriesComponent"
},
"products": {
"type": "Component",
"usage": "productsComponent"
},
...
}
}
}
}

 

Note how...

  • components are declared in the sap.ui5.componentUsages section

  • routes have a prefix property assigned

  • targets are defined with type "Component" and usage referring to a component from the sap.ui5.componentUsages section.


This all works pretty well. For example when I select the menu option for suppliers the hash value changes to #/suppliers (which matches the pattern defined in my manifest) and the Suppliers component is loaded.

When I select one of the entries in the table the hash value changes to something like #/suppliers&/s/detail/2. "s" is the alias defined for the suppliers target so the '/detail/2' section of the hash is passed to the component router which triggers navigation to the Detail view of the Product component and binds to product 2.

Browser back and forward buttons work too. If I do a browser refresh I am returned to the same views with the same context.

What about navigation between subcomponents?

I enhanced my Suppliers Detail view with a sap.m.Table control that shows the products provided by the current supplier.



As above - I added component details for the Products component to the manifest file for the Suppliers component and a suitable routing target to navigate to the selected Product.
      "routes": [
...
{
"name": "products",
"pattern": "products/{id}",
"target": {
"name": "products",
"prefix": "p"
}
},
...
],

Problem - I found that when I select a product from the Supplier Details view to trigger navigation to the Details view of the Products component it goes to the List view instead.

*Note - see follow-up blog UI5 - Navigate with Nested Components

The Supplier component router does not pass the route pattern through to the Product component router. So even though the hash changes to something like #/products/4 the subcomponent router sees an empty pattern and, in our example, loads the List view because it matches that pattern.
			"routes": [
{
"name": "list",
"pattern": "",
"target": "list"
},
...

So instead of the details for product 4 I see a list of all products.

I tried playing around with routing patterns to get this to work out-of-the-box but really couldn't make any progress. As Tom refers to in his blog a & symbol in a routing pattern does not play nice.

FYI - I have raised an issue about this routing issue with nested components. SAP have advised that others have requested this feature so I hope we can expect it to be available in the future. https://github.com/SAP/openui5/issues/2610

In the meantime I have found a work-around. The subcomponent router oHashChanger has a parent property that points to the oHashChanger of the main router - and from that I can get the hash value from the parent component and use it to trigger immediate navigation to the Detail view.

In the List controller I attach a PatternMatched event handler to the router to do this.
		onInit: function() {
Controller.prototype.onInit.apply(this, arguments)

this.getOwnerComponent()
.getRouter()
.getRoute("list")
.attachPatternMatched(this._onPatternMatched, this)
},
_onPatternMatched: function() {
Controller.prototype.onInit.apply(this, arguments)

const oRouter = this.getOwnerComponent().getRouter()
try {
const aHash = oRouter.oHashChanger.parent.hash.split("/")
if (aHash.length > 1) {
switch (aHash[0]) {
case "products":
oRouter.navTo(
"detail",
{
id: aHash[1]
},
true
)
break
default:
}
}
} catch {}
},
...

The event handler needs to know the routing pattern the parent component router uses - so it is not ideal - but it works pretty well.

This means we can now navigate back and forth across all the subcomponents. From Supplier to Products to Category to another Product to Supplier etcetera.

Notice that the hash pattern starts to get pretty interesting after a few cross-component navigations. e.g. #/suppliers&/s/products/31&/s-p/categories/4&/s-p-c/products/59&/s-p-c-p/suppliers/28&/s-p-c-p-s/detail/28

Browser back, forward and refresh also work well.

Enjoy!

*Note Sample code is at https://github.com/Yelcho/UI5-Comp-Routing

*News - note SAP are addressing the routing issue I raise here. See https://github.com/SAP/openui5/issues/2610 for details.

*See follow-up blog UI5 - Navigate with Nested Components
6 Comments
Labels in this area