<!DOCTYPE html>
<html style="height: 100%;">
  <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-resource-roots='{
        "demo": "./",
        "demo.shared": "https://run.plnkr.co/plunks/8aLaog6TMWiYno8Z/"
      }'
      data-sap-ui-xx-component-preload="off"
      data-sap-ui-xx-wait-for-theme="init"
    ></script>
  </head>
  <body id="content" class="sapUiBody">
    <div style="height: 100%;"
      data-sap-ui-component
      data-id="rootComponentContainer"
      data-name="demo"
      data-height="100%"
      data-settings='{"id": "rootComponent"}'
    ></div>
  </body>
</html>
sap.ui.define([
  "sap/ui/core/UIComponent",
  "sap/ui/Device",
  "sap/ui/core/EventBus",
], function(UIComponent, Device) {
  "use strict";

  return UIComponent.extend("demo.Component", {
    metadata: {
      manifest: "json",
      interfaces: [
        "sap.ui.core.IAsyncContentCreation",
      ],
    },

    init: function() {
      UIComponent.prototype.init.apply(this, arguments);
      this.getModel("odata").metadataLoaded(true).catch(() => sap.ui.require([
        "demo/shared/corsPromptMessage"
      ]));
      this.getRouter().initialize();
      this.getEventBus().subscribe("detail", "loaded", this.detailLoaded, this);
    },

    detailLoaded: function() {
      this.getRootControl().byId("rootSplitContainer").hideMaster();
    },

  });
});
{
  "_version": "1.16.0",
  "sap.app": {
    "id": "demo",
    "type": "component",
    "i18n": "",
    "title": "Demo",
    "description": "Sample Code",
    "applicationVersion": {
      "version": "1.0.0"
    },
    "dataSources": {
      "odataDemo": {
        "uri": "https://cors-anywhere.herokuapp.com/https://services.odata.org/V2/Northwind/Northwind.svc/",
        "type": "OData",
        "settings": {
          "odataVersion": "2.0"
        }
      }
    }
  },
  "sap.ui": {
    "technology": "UI5",
    "deviceTypes": {
      "desktop": true,
      "tablet": true,
      "phone": true
    }
  },
  "sap.ui5": {
    "autoPrefixId": true,
    "dependencies": {
      "minUI5Version": "1.73.1",
      "libs": {
        "sap.ui.core": {},
        "sap.m": {}
      }
    },
    "models": {
      "odata": {
        "dataSource": "odataDemo",
        "settings": {
          "tokenHandling": false,
          "preliminaryContext": true,
          "canonicalRequest": true
        },
        "preload": true
      }
    },
    "contentDensities": {
      "compact": true,
      "cozy": true
    },
    "rootView": {
      "viewName": "demo.view.Root",
      "id": "rootView",
      "type": "XML",
      "async": true
    },
    "routing": {
      "routes": {
        "master": {
          "pattern": "",
          "name": "master",
          "target": ["detail", "master"]
        },
        "masterDetail": {
          "pattern": "items/{itemId}",
          "name": "masterDetail",
          "target": ["master", "detail"]
        }
      },
      "targets": {
        "master": {
          "viewId": "masterView",
          "viewName": "Master",
          "controlAggregation": "masterPages",
          "viewLevel": 1
        },
        "detail": {
          "viewId": "detailView",
          "viewName": "Detail",
          "controlAggregation": "detailPages",
          "transition": "baseSlide",
          "viewLevel": 2
        }
      },
      "config": {
        "routerClass": "sap.m.routing.Router",
        "async": true,
        "viewType": "XML",
        "viewPath": "demo.view",
        "controlId": "rootSplitContainer"
      }
    }
  }
}
<mvc:View controllerName="demo.controller.Detail"
  xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m"
  xmlns:core="sap.ui.core"
  core:require="{ EdmDateTime: 'sap/ui/model/odata/type/DateTime' }"
>
  <Page id="page" 
    class="sapUiNoContentPadding"
    busyIndicatorDelay="0"
    contentOnlyBusy="true"
    title="Product: {odata>ProductName}"
    floatingFooter="true"
    showNavButton="{:= !${device>/system/desktop}}"
    navButtonPress=".navBack($controller.getOwnerComponent().getRouter())"
  >
    <Table
      busyIndicatorDelay="0"
      sticky="ColumnHeaders"
      growing="true"
      growingScrollToLoad="true"
      updateFinished=".onBindSuccess"
      items="{
        path: 'odata>Order_Details',
        templateShareable: false
      }"
    >
      <columns>
        <Column width="25%">
          <Text text="Order ID" />
        </Column>
        <Column width="25%"
          popinDisplay="WithoutHeader"
          demandPopin="true"
          minScreenWidth="Tablet"
          hAlign="Right"
        >
          <Text text="Order Date" />
        </Column>
        <Column width="auto">
          <Text text="Customer" />
        </Column>
      </columns>
      <ColumnListItem>
        <Text text="{odata>Order/OrderID}"/>
        <Text text="{
          path: 'odata>Order/OrderDate',
          type: 'EdmDateTime',
          constraints: {
            displayFormat: 'Date'
          }
        }"/>
        <Text text="{odata>Order/Customer/CompanyName}"/>
      </ColumnListItem>
    </Table>
    <footer>
      <!-- ... -->
    </footer>
  </Page>
</mvc:View>
<mvc:View controllerName="demo.controller.Master"
  xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m">
  <Page title="Products from UK"
    class="sapUiResponsivePadding--header"
    backgroundDesign="List"
  >
    <List id="masterList"
      busyIndicatorDelay="0"
      growing="true"
      updateFinished=".displayDetailsOfFirstItem($event.getSource())"
      items="{
        path: 'odata>/Products',
        templateShareable: false,
        parameters: {
          expand: 'Category, Supplier',
          select: 'ProductID, ProductName, UnitsInStock, Category/CategoryName, Supplier/Country'
        },
        sorter: [
          {
            path: 'Category/CategoryName',
            group: true
          },
          {
            path: 'ProductName'
          }
        ],
        filters: [
          {
            path: 'Supplier/Country',
            operator: 'EQ',
            value1: 'UK'
          }
        ]
      }"
    >
      <ObjectListItem type="Active"
        title="{odata>ProductName}"
        number="x {odata>UnitsInStock}"
        press=".navToDetailOf(${odata>ProductID})"
      />
    </List>
  </Page>
</mvc:View>
<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" height="100%">
  <SplitApp id="rootSplitContainer">
    <masterPages>
      <!-- will be placed by the routing -->
    </masterPages>
    <detailPages>
      <!-- will be placed by the routing -->
    </detailPages>
  </SplitApp>
</mvc:View>
sap.ui.define([
  "sap/ui/core/mvc/Controller",
  "sap/ui/core/routing/History",
], function(Controller, History) {
  "use strict";

  return Controller.extend("demo.controller.Detail", {
    onInit: function() {
      const router = this.getOwnerComponent().getRouter(); 
			const route = router.getRoute("masterDetail");
			route.attachPatternMatched(this.onPatternMatched, this);
    },

    onPatternMatched: async function(event) {
      const model = this.getOwnerComponent().getModel("odata");
      const { itemId } = event.getParameter("arguments");
      const itemIdDecoded = window.decodeURIComponent(itemId);
      await model.metadataLoaded();
      this.bindSelectedItem(itemIdDecoded);
    },
    
    bindSelectedItem: function(key1) {
      const model = this.getOwnerComponent().getModel("odata");
      const key = model.createKey("Products", {
        ProductID: key1, // creating key(s) dynamically.
        // See https://stackoverflow.com/a/47016070/5846045
      });
      // model.invalidateEntry(key); for https://stackoverflow.com/a/59398119/5846045
      this.getView().bindObject({
        path: "odata>/" + key,
        parameters: {
          expand: [
            "Order_Details/Order/Customer",
          ].join(",")
        },
      });
    },

    onBindSuccess: function() {
      const eventBus = this.getOwnerComponent().getEventBus();
      eventBus.publish("detail", "loaded");
    },

    // Only for mobile case
    navBack: router => History.getInstance().getPreviousHash() !== undefined
      ? window.history.go(-1)
      : router.navTo("master", {}, true),
  
  });
});
sap.ui.define([
  "sap/ui/core/mvc/Controller",
  "sap/ui/Device",
], function(Controller, Device) {
  "use strict";

  return Controller.extend("demo.controller.Master", {
    navToDetailOf: function(itemId) {
      const router = this.getOwnerComponent().getRouter();
      const itemIdEncoded = window.encodeURIComponent(itemId);
      router.navTo("masterDetail", {
        itemId: itemIdEncoded
      }, !Device.system.phone);
    },

  });
});