<!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-onInit="module:sap/ui/core/ComponentSupport"
data-sap-ui-async="true"
data-sap-ui-compatVersion="edge"
data-sap-ui-excludeJQueryCompat="true"
data-sap-ui-resourceRoots='{ "my.demo": "./" }'
data-sap-ui-xx-waitForTheme="init"
data-sap-ui-xx-componentPreload="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.
// Refer also to the documentation of FLP and Fiori elements to learn how to handle unsaved changes.
}
},
});
});
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 });
});
},
});
});