Routing in UI5/Angular hybrids
In a previous blog post we presented a PoC for combining stand-alone web applications written in different technology stacks.
The mentioned use case consisted of an Angular web application which embedded a UI5 web application. This blog post goes further and examines the interaction and side effects of the hosting Angular router and the embedded UI5 router. This becomes especially relevant when both routers use a hash based routing scheme and therefore interfere during navigation. We present a way for co-existing routing when using an embedded UI5 web application in an Angular web application with a hash based routing.
Hash based routing
A typical approach for SPAs is to use a so-called hash based routing. In this case the base / anchor part of the URL remains unchanged and only a suffix containing of a # followed by a relative path is used to identify the target page which should be displayed. The router then usually listens to changes to this suffix and determines according to its configuration which is usually a mapping of the suffix to a target page. A programmatical navigation therefore only consists of a change to the hash which triggers the router interaction. This is where it might get to problems when two routers work with this hash based scheme and both are registered to listen to location changes of the browser which should be investigated in the next chapter.
Concurring hash based routers
The setup we used was an Angular web application with a hash based router which was configured to have bunch of routes. One of these routes is the special launchpad component which itself bootstrapped a given UI5 component/web application. The UI5 web application also has a router of its own with a separate configuration.
Adaptions to the router
In context of this blog post, the latter solution was chosen and the embedded UI5 web application obtained an adapted router.
The so-called "PrefixedRouter" inherits the default router and overwrites the "parse" function. The function "_unprefixHash" removes an optional prefix and passes the new hash to the usual application lifecycle.
parse : function (sNewHash) { if (this._oRouter) { this._oRouter.parse(this._unprefixHash(sNewHash)); } else { Log.warning("This router has been destroyed while the hash changed. No routing events where fired by the destroyed instance.", this); } }
Additionally, the manifest file of the UI5 web application must refer to this adapted router:
"routing": { "config": { "routerClass": "....PrefixedRouter", ...
Implications of the UI5 HashChanger
Despite the adaptions to the router the UI5 runtime environment seems to instantiate several singletons which contribute to the routing functionality of the hosted UI5 applications. Especially the instance of the HashChanger class needs special handling when embedding UI5 web applications in this manner. When providing query parameters in the URL the default lifecycle seems to lack an update of the hash changer. Although the URL contains parameters the router of the embedded UI5 web application does not receive this data leading to erroneous behaviour.
Therefore, the implementation of the hosting component adapter in the Angular web application needs to consider this fact. The current implementation of the UI5 launchpad component triggers the reparsing of the location by triggering a HashChanger specific event.
_forceHashChangerUpdate() { if (!sap.ui.core.routing.HashChanger.getInstance) { return; } let hashChanger = sap.ui.core.routing.HashChanger.getInstance(); let hash = safeDecodeURIComponent(window.location.hash.startsWith('#/') ? window.location.hash.substring(2) : window.location.hash); hashChanger.fireHashChanged(hash, hashChanger.getHash()); }
Conclusion
The implementation of this PoC proves that it is possible to maintain two concurrent hash based routers in a multi framework scenario. Although the impacts to the involved applications are limited to a minimum this approach imposes a dependence between those applications. Therefore, if applicable, it is recommended to configure at least one router not to use a hash based addressing scheme.
References
The content of this article was presented in the ui5con 2020 conference. The leading conference for ui5. (https://openui5.org/ui5con/onair2020/#agenda). The sourcecode for the PoCs is available at Github:
Image sources
The cover image used in this post was created by wikimedia under the following license. All other images on this page were created by eXXcellent solutions under the terms of the Creative Commons Attribution 4.0 International License