<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Demo</title>
    <script id="sap-ui-bootstrap"
      src="https://sdk.openui5.org/nightly/resources/sap-ui-core.js"
      data-sap-ui-on-init="module:sap/ui/core/ComponentSupport"
      data-sap-ui-async="true"
      data-sap-ui-compat-version="edge"
      data-sap-ui-exclude-jquery-compat="true"
      data-sap-ui-resource-roots='{ "my.demo": "./" }'
      data-sap-ui-xx-wait-for-theme="init"
      data-sap-ui-xx-component-preload="off"
    ></script>
  </head>
  <body id="content" class="sapUiBody">
    <div data-sap-ui-component
      data-id="rootComponentContainer"
      data-name="my.demo"
      data-height="100%"
      data-settings='{"id": "rootComponent"}'
    ></div>
  </body>
</html>
{
	"_version": "1.60.0",
	"start_url": "index.html",
	"sap.app": {
		"id": "my.demo",
		"title": "foo",
		"type": "application",
		"applicationVersion": {
			"version": "1.0.0"
		},
		"dataSources": {}
	},
	"sap.ui": {
		"technology": "UI5",
		"deviceTypes": {
			"desktop": true,
			"tablet": true,
			"phone": true
		}
	},
	"sap.ui5": {
		"dependencies": {
			"minUI5Version": "1.120",
			"libs": {
				"sap.ui.core": {},
				"sap.m": {},
				"sap.ui.layout": {},
				"sap.ui.unified": {}
			}
		},
		"contentDensities": {
			"compact": true,
			"cozy": true
		},
		"flexEnabled": false,
		"config": {
			"sapFiori2Adaptation": true
		},
		"models": {
			"nav": {
				"type": "sap.ui.model.json.JSONModel"
			}
		},
		"rootView": {
			"id": "rootView",
			"viewName": "my.demo.view.App",
			"type": "XML"
		},
		"routing": {
			"routes": [
				{
					"name": "home",
					"pattern": "",
					"target": "home"
				},
				{
					"name": "next",
					"pattern": "next",
					"target": "next"
				}
			],
			"targets": {
				"home": {
					"id": "homeView",
					"name": "Home",
					"level": 1,
					"title": "Home"
				},
				"next": {
					"id": "nextView",
					"name": "Next",
					"level": 2,
					"title": "Next"
				},
				"notFound": {
					"id": "notFoundView",
					"name": "Home",
					"transition": "show"
				}
			},
			"config": {
				"routerClass": "sap.m.routing.Router",
				"type": "View",
				"viewType": "XML",
				"async": true,
				"path": "my.demo.view",
				"transition": "slide",
				"controlId": "myApp",
				"controlAggregation": "pages",
				"bypassed": {
					"target": "notFound"
				}
			}
		}
	}
}
<mvc:View controllerName="my.demo.controller.App"
	xmlns:mvc="sap.ui.core.mvc"
	xmlns="sap.m"
	displayBlock="true"
>
	<!--autoFocus="false" for https://stackoverflow.com/a/48559689/5846045-->
	<!--navigate=".onNavigate" for https://stackoverflow.com/a/59091125/5846045-->
	<App id="myApp" autoFocus="false" navigate=".onNavigate">
		<pages>
			<!-- will be managed by the router -->
		</pages>
	</App>
</mvc:View>
<mvc:View controllerName="my.demo.controller.Home"
  xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m"
  xmlns:layout="sap.ui.layout"
  xmlns:core="sap.ui.core"
  height="100%"
>
  <Page id="homePage"
    class="sapUiResponsivePadding--header sapUiResponsivePadding--content"
    title="Home"
  >
    <headerContent>
      <core:Fragment
        fragmentName="my.demo.view.fragment.PreventNav"
        type="XML"
      />
    </headerContent>

    <!-- https://stackoverflow.com/a/48559689/5846045 -->
    <layout:VerticalLayout class="sapUiSmallMarginTop">
      <Button
        width="100%"
        type="Ghost"
        text="Navigate"
        icon="sap-icon://navigation-right-arrow"
        iconFirst="false"
        press=".navToNext"
      />
      <Input id="target1" placeholder="Focus target 1" />
    </layout:VerticalLayout>

    <!-- https://github.com/SAP/openui5/issues/3602 -->
    <form:SimpleForm xmlns:form="sap.ui.layout.form"
      editable="true"
      layout="ColumnLayout"
      backgroundDesign="Transparent"
    >
      <Label text="Input within Form" />
      <Input id="target2" placeholder="Focus target 2" />
    </form:SimpleForm>
  </Page>
</mvc:View>
<mvc:View controllerName="my.demo.controller.Next"
  xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m"
  xmlns:core="sap.ui.core"
  height="100%"
>
  <Page id="nextPage"
    class="sapUiResponsivePadding--header sapUiResponsivePadding--content"
    showNavButton="true"
    title="Navigated View"
    navButtonPress=".navBack($controller.getOwnerComponent().getRouter())"
  >
    <headerContent>
      <core:Fragment
        fragmentName="my.demo.view.fragment.PreventNav"
        type="XML"
      />
    </headerContent>
    <layout:VerticalLayout xmlns:layout="sap.ui.layout"
      class="sapUiTinyMarginTop"
      width="100%"
    >
      <Text text="↑ Nav back" class="sapUiTinyMarginBegin" />
    </layout:VerticalLayout>
  </Page>
</mvc:View>
<ToggleButton xmlns="sap.m"
  text="Prevent Navigation ✋"
  pressed="{nav>/prevent}"
  type="Ghost"
/>
sap.ui.define([
  "sap/ui/core/UIComponent",
  "sap/ui/core/ComponentSupport",//https://github.com/SAP/ui5-tooling/issues/381
], function(UIComponent) {
  "use strict";
  
  const uriParams = new URLSearchParams(document?.location?.search);
  return UIComponent.extend("my.demo.Component", {
    metadata: {
      interfaces: [
        "sap.ui.core.IAsyncContentCreation",
      ],
      manifest: "json",
    },
    
    init: function() {
      UIComponent.prototype.init.apply(this, arguments);
      window.addEventListener("beforeunload", this.onBeforeLeave.bind(this));
      const preventNav = uriParams?.get("prevent-nav")?.trim()?.toLowerCase();
      this.getModel("nav").setProperty("/prevent", preventNav === "true");
      this.getRouter()
        .attachBeforeRouteMatched(this.onBeforeRouteMatched, this)
        .initialize();
    },

    onBeforeRouteMatched: function(event) {
      // Future approach? Discussion: https://github.com/SAP/openui5/issues/3411
      if (this.getModel("nav").getProperty("/prevent")) {
        event.preventDefault();
      }
    },

    onBeforeLeave: function(event) {
      if (this.getModel("nav").getProperty("/prevent")) {
        // Display confirmation dialog
        event.preventDefault(); // as specified by the HTML standard
        event.returnValue = ""; // as required by Chromium browsers
      }
    },

  });
});
sap.ui.define([
  "sap/ui/core/mvc/Controller",
], function(Controller) {
  "use strict";

  const uriParams = new URLSearchParams(document?.location?.search);
  return Controller.extend("my.demo.controller.Home", {
    onInit: function() {
      const target = uriParams?.get("initial-focus")?.trim()?.toLowerCase();
      this.applyInitialFocusTo(this.byId(target || "target2"), this.getView());
    },

    applyInitialFocusTo: function(target, navContainerChild) {
      // https://stackoverflow.com/a/48559689/5846045
      // https://github.com/SAP/openui5/issues/2306
      // https://github.com/SAP/openui5/issues/3602
      const onAfterShow = () => target && target.focus();
      navContainerChild.addEventDelegate({ onAfterShow });
    },

    navToNext: function() {
      this.getOwnerComponent().getRouter().navTo("next");
    },

  });
});
sap.ui.define([
  "sap/ui/core/mvc/Controller",
  "sap/ui/core/routing/History",
], function(Controller, History) {
  "use strict";

  return Controller.extend("my.demo.controller.Next", {
    onInit: function() {
      this._navDelegate = { onBeforeHide: this.onBeforeLeave };
      this.getView().addEventDelegate(this._navDelegate, this);
    },

    onBeforeLeave: function() {
      // https://stackoverflow.com/a/56852018/5846045
      console.log("Perform task before hiding the 'Next' view ...");
    },

    navBack: router => History.getInstance().getPreviousHash() !== undefined
      ? window.history.go(-1) // Navigate back
      : router.navTo("home"), // Navigate up

    onExit: function() {
      // Detach events, delegates, and references to avoid memory leak
      this.getView().removeEventDelegate(this._navDelegate);
      this._navDelegate = null;
    },

  });
});
sap.ui.define([
  "sap/ui/core/mvc/Controller",
], function(Controller) {
  "use strict";

  return Controller.extend("my.demo.controller.App", {
    onNavigate: function(event) {
      // https://stackoverflow.com/a/59091125/5846045
      const navModel = this.getOwnerComponent().getModel("nav");
      if (navModel.getProperty("/prevent")) {
        event.preventDefault();
        const { isBack, isBackToPage, isBackToTop } = event.getParameters();
        if (isBack || isBackToPage || isBackToTop) {
          window.history.go(1);
          this.displayMessage("There is no escape. 👁️");
        } else {
          window.history.go(-1);
          this.displayMessage("One does not simply walk into Mordor. 👁️");
        }
      }
    },

    displayMessage: function(message) {
      sap.ui.require([ "sap/m/MessageBox" ], function(MessageBox) {
        MessageBox.alert(message, { closeOnNavigation: false });
      });
    },

  });
});