At SAP Inside Track Hamburg 2016 together with Remo Bettin we demonstrated during a short lightning talk how to do Cross Application Navigation between different applications on the Fiori Launchpad.

While this topic is covered in SAPUI5 help it is still not so easy to implement it. Therefore we built a small demo application in order to demonstrate how to use it.

Based on the entities of the Northwind OData Service we created four separate SAPUI5 Applications, one for orders, others for the corresponding customers and suppliers of the orders shown in the Orders app. The last application we used is based on the categories entity of the Northwind service.


All these applications are added to the Fiori Launchpad in the HANA Cloud Platform (which is named flpnwc there and available as a subscription in the HCP account).

Bildschirmfoto 2016-06-15 um 22.03.16.png

Picture 1: Fiori Launchpad in HCP showing the applications

In order to be able to use cross app navigation all applications need to be assigned to intents. The intent consists of a semantic object and an action on this object. For our applications we used the entity name as semantic object and all applications use the “display” action to display some data. The semantic object is declared in the manifest.json from the SAPUI5 app.

Bildschirmfoto 2016-06-20 um 15.26.11.png

Picture 2: Navigation properties in manifest.json

Once an application is reached it uses inner app navigation to find the correct route to a view.

The following example shows the routes in the supplier app. Here to navigate to a supplier detail we use a route “Suppliers/5” in order to open the object view and show details of supplier with ID 5.

Bildschirmfoto 2016-06-20 um 15.23.49.png

Picture 3: Routes in manifest.json

In order to navigate from one application to another in the Fiori Launchpad (FLP) we need to implement some code at the following locations:

1. The starting point of our CrossApp-Navigation is pressing on the supplier column in the Orders app. There we added the onPressSupplier-event in the column of the detail view where the supplier is shown


onPressSupplier: function(oEvent) {
  var supplier = oEvent.getSource().getBindingContext().getProperty("Product/SupplierID"); // read SupplierID from OData path Product/SupplierID
  var oCrossAppNavigator = sap.ushell.Container.getService("CrossApplicationNavigation"); // get a handle on the global XAppNav service
  var hash = (oCrossAppNavigator && oCrossAppNavigator.hrefForExternal({
  target: {
  semanticObject: "Supplier",
  action: "display"
  },
  params: {
  "supplierID": supplier
  }
  })) || ""; // generate the Hash to display a Supplier
  oCrossAppNavigator.toExternal({
  target: {
  shellHash: hash
  }
  }); // navigate to Supplier application
  }




2. In the onInit-Event of the Master view of the Suppliers App we attach the event _onMasterMatched:


this.getRouter().getRoute("master").attachPatternMatched(this._onMasterMatched, this);

3. In the _onMasterMatched we read the Startup parameters where we get the Supplier ID:



_onMasterMatched :  function() {
  var supplierID;
  var startupParams = this.getOwnerComponent().getComponentData().startupParameters; // get Startup params from Owner Component
  if ((startupParams.supplierID && startupParams.supplierID[0])) {
  this.getRouter().navTo("object", {
  supplierID: startupParams.supplierID[0]  // read Supplier ID. Every parameter is placed in an array therefore [0] holds the value
  }, true);
  } else {
  this.getOwnerComponent().oListSelector.oWhenListLoadingIsDone.then(
  function(mParams) {
  if (mParams.list.getMode() === "None") {
  return;
  }
  supplierID = mParams.firstListitem.getBindingContext().getProperty("SupplierID");
  this.getRouter().navTo("object", {
  supplierID: supplierID
  }, true);
  }.bind(this),
  function(mParams) {
  if (mParams.error) {
  return;
  }
  this.getRouter().getTargets().display("detailNoObjectsAvailable");
  }.bind(this)
  );
  }



Demonstration

Based on the entities of the Northwind OData Service we created four separate SAPUI5 Applications. In the Orders application you see a Master-Detail navigation for selecting an order and displaying the corresponding order details.


We select one order in the left master view and after that click on Customer “Vins et alcools Chevalier” with CustomerID “VINET”:

Bildschirmfoto 2016-06-15 um 22.10.32.png

This creates the link “#Customer-display?customerID=VINET&/Customers/VINET”.

The action “display” in the application with semantic object “Customer” is invoked.

The startup parameter is “customerID=VINET”. This invokes the inner app navigation in the Customer app resolving in the URL fragment “/Customers/VINET” which is the route to the object view and shows the details view of customer “Vins et alcools Chevalier”.

Bildschirmfoto 2016-06-15 um 22.11.26.png

Similar routing happens when clicking on the supplier in the orders app:

Bildschirmfoto 2016-06-15 um 22.12.02.png

Question

When using the sap.ushell.services.CrossApplicationNavigation.toExternal() method you can optionally provide a component (the root component of the application). Unfortunately in the help you can’t find a hint for what this could be used. Maybe somebody knows the intent of this parameter?

Hopefully this blog post will be of some help for your implementation of Cross application navigation. In case you still have questions I would be happy to answer them if possible.

Cheers,

Mark

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Viswa M

    Can you provide the manifest.json code? I have implemented same code in my system but i did not understand mainfest.json code in your blog

    cross application navigation not working in my application and console did not provided any error.  i did not able to figureout error..

    (0) 
    1. Mark Teichmann

      Sorry for my late response. I did not get any notification from the new community platform…

       

      This is the manifest.json from one of the apps.

      {
      	"_version": "1.3.0",
      	"sap.app": {
      		"_version": "1.3.0",
      		"id": "sithhorders",
      		"type": "application",
      		"i18n": "i18n/i18n.properties",
      		"title": "{{appTitle}}",
      		"description": "{{appDescription}}",
      		"applicationVersion": {
      			"version": "1.0.0"
      		},
      		"dataSources": {
      			"mainService": {
      				"uri": "/destinations/northwind/V2/Northwind/Northwind.svc/",
      				"type": "OData",
      				"settings": {
      					"odataVersion": "2.0",
      					"localUri": "localService/metadata.xml"
      				}
      			}
      		},
      		"sourceTemplate": {
      			"id": "sap.ui.ui5-template-plugin.2masterdetail",
      			"version": "1.36.2"
      		},
      		"crossNavigation": {
      			"inbounds": {
      				"intent1": {
      					"signature": {
      						"parameters": {},
      						"additionalParameters": "allowed"
      					},
      					"semanticObject": "Orders",
      					"action": "display",
      					"title": "Orders",
      					"icon": "sap-icon://sales-order"
      				}
      			}
      		}
      	},
      	"sap.ui": {
      		"_version": "1.3.0",
      		"technology": "UI5",
      		"icons": {
      			"icon": "sap-icon://detail-view",
      			"favIcon": "",
      			"phone": "",
      			"phone@2": "",
      			"tablet": "",
      			"tablet@2": ""
      		},
      		"deviceTypes": {
      			"desktop": true,
      			"tablet": true,
      			"phone": true
      		},
      		"supportedThemes": [
      			"sap_hcb",
      			"sap_bluecrystal"
      		]
      	},
      	"sap.ui5": {
      		"_version": "1.2.0",
      		"rootView": {
      			"viewName": "sithhorders.view.App",
      			"type": "XML",
      			"id": "app"
      		},
      		"dependencies": {
      			"minUI5Version": "1.36.0",
      			"libs": {
      				"sap.ui.core": {
      					"minVersion": "1.36.0"
      				},
      				"sap.m": {
      					"minVersion": "1.36.0"
      				},
      				"sap.suite.ui.microchart": {
      					"minVersion": "1.36.0"
      				}
      			}
      		},
      		"contentDensities": {
      			"compact": true,
      			"cozy": true
      		},
      		"models": {
      			"i18n": {
      				"type": "sap.ui.model.resource.ResourceModel",
      				"settings": {
      					"bundleName": "sithhorders.i18n.i18n"
      				}
      			},
      			"": {
      				"dataSource": "mainService",
      				"settings": {
      					"metadataUrlParams": {
      						"sap-documentation": "heading"
      					},
      					"useBatch": false
      				}
      			}
      		},
      		"routing": {
      			"config": {
      				"routerClass": "sap.m.routing.Router",
      				"viewType": "XML",
      				"viewPath": "sithhorders.view",
      				"controlId": "idAppControl",
      				"controlAggregation": "detailPages",
      				"bypassed": {
      					"target": [
      						"master",
      						"notFound"
      					]
      				},
      				"async": true
      			},
      			"routes": [
      				{
      					"pattern": "",
      					"name": "master",
      					"target": [
      						"object",
      						"master"
      					]
      				},
      				{
      					"pattern": "Orders/{objectId}",
      					"name": "object",
      					"target": [
      						"master",
      						"object"
      					]
      				}
      			],
      			"targets": {
      				"master": {
      					"viewName": "Master",
      					"viewLevel": 1,
      					"viewId": "master",
      					"controlAggregation": "masterPages"
      				},
      				"object": {
      					"viewName": "Detail",
      					"viewId": "detail",
      					"viewLevel": 2
      				},
      				"detailObjectNotFound": {
      					"viewName": "DetailObjectNotFound",
      					"viewId": "detailObjectNotFound"
      				},
      				"detailNoObjectsAvailable": {
      					"viewName": "DetailNoObjectsAvailable",
      					"viewId": "detailNoObjectsAvailable"
      				},
      				"notFound": {
      					"viewName": "NotFound",
      					"viewId": "notFound"
      				}
      			}
      		},
      		"resources": {
      			"js": []
      		}
      	},
      	"sap.platform.hcp": {
      		"uri": "webapp",
      		"_version": "1.1.0"
      	}
      }
      (0) 
    1. Mark Teichmann

      Hi Charles,

       

      it is only a warning and will not cause an issue. But from a coding perspective it would be nicer to write it like the following example:

      checkPromoFactSheetAvailable : function() {
          // By default: promo factsheet not available
          this._bPromoFactSheetAvailable = false;
          if (this._oCrossAppNav) {
              // Check if the intent for the promotion factsheet is supported
              var sIntent = "#Promotion-displayFactSheet";
              var oDeferred = this._oCrossAppNav.isIntentSupported([sIntent]);
              oDeferred.done(jQuery.proxy(function(oIntentSupported) {
                  if (oIntentSupported && oIntentSupported[sIntent] && oIntentSupported[sIntent].supported === true) {
                      // Remember that the navigation to the promotion factsheet is possible
                      this._bPromoFactSheetAvailable = true;
                      // Activate the promotion links if they were already added to the view
                      this.activatePromotionLinks();
                  }
              }, this));
          }

      Source: https://help.hana.ondemand.com/webide/frameset.htm?6de3ec7327e0432cae87fc83f06db827.html

      (0) 

Leave a Reply