<!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='{ "demo": "./" }'
      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>
{
  "_version": "1.60.0",
  "start_url": "index.html",
  "sap.app": {
    "id": "demo",
    "type": "application",
    "title": "foo",
    "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": {
    "dependencies": {
      "minUI5Version": "1.120",
      "libs": {
        "sap.ui.core": {},
        "sap.m": {},
        "sap.ui.layout": {},
        "sap.ui.unified": {}
      }
    },
    "contentDensities": {
      "compact": true,
      "cozy": true
    },
    "models": {
      "": {
        "dataSource": "odataDemo",
        "settings": {
          "tokenHandling": false,
          "preliminaryContext": true,
          "canonicalRequests": true,
          "defaultCountMode": "None",
          "useBatch": false
        },
        "preload": true
      }
    },
    "rootView": {
      "id": "rootView",
      "viewName": "demo.view.App",
      "type": "XML"
    },
    "routing": {
      "routes": [
        {
          "pattern": "",
          "name": "home",
          "target": "home"
        }
      ],
      "targets": {
        "home": {
          "id": "homeView",
          "name": "Home",
          "level": 1
        },
        "notFound": {
          "id": "notFoundView",
          "name": "Home",
          "transition": "show"
        }
      },
      "config": {
        "routerClass": "sap.m.routing.Router",
        "type": "View",
        "viewType": "XML",
        "path": "demo.view",
        "transition": "slide",
        "controlId": "myApp",
        "controlAggregation": "pages",
        "bypassed": {
          "target": "notFound"
        }
      }
    }
  }
}
<mvc:View xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m"
  displayBlock="true"
	height="100%"
>
	<App id="myApp" autoFocus="true">
	  <pages>
	    <!-- will be placed in here by router targets -->
	  </pages>
	</App>
</mvc:View>
<mvc:View controllerName="demo.controller.Home"
  xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m"
>
  <Page
    title="Customers"
    backgroundDesign="List"
  >
    <headerContent>
      <SearchField id="searchField"
        search=".onSearch"
        placeholder="Country or City"
      />
    </headerContent>
    <List id="list"
      growing="true"
      growingScrollToLoad="true"
    >
      <items>
        <!-- will be bound in the controller -->
      </items>
    </List>
  </Page>
</mvc:View>
sap.ui.define([
  "sap/ui/core/UIComponent",
], (UIComponent) => {
  "use strict";
  
  return UIComponent.extend("demo.Component", {
    metadata: {
      manifest: "json",
      interfaces: [
        "sap.ui.core.IAsyncContentCreation",
      ],
    },

    init() {
      UIComponent.prototype.init.apply(this, arguments);
      this.getModel().metadataLoaded(true)
        .catch(() => sap.ui.require([
          "sap/m/Dialog",
          "sap/m/FormattedText",
          "sap/m/Link",
        ], (Dialog, FormattedText, Link) => (new Dialog({
          type: "Message",
          state: "Information",
          showHeader: false,
          content: [
            new FormattedText({
              htmlText: `<p>To allow the OData V2 service:<ol><li>Visit %%0.</li><li>Click on the button "Request temporary access to the demo server".</li><li>Reload this Preview page.</li></ol></p>`,
              controls: [
                new Link({
                  href: "https://cors-anywhere.herokuapp.com/",
                  target: "_blank",
                  text: "cors-anywhere.herokuapp.com"
                })
              ]
            })
          ]
        })).open()))
        .then(() => this.getRouter().initialize());
    },

  });
});
sap.ui.define([
  "sap/ui/core/mvc/Controller",
  "sap/m/StandardListItem",
  "sap/ui/model/Filter",
  "sap/ui/model/FilterOperator",
  "sap/ui/model/FilterType",
], (Controller, StandardListItem, Filter, FilterOperator, FilterType) => {
  "use strict";

  return Controller.extend("demo.controller.Home", {
    onInit() {
      this.bindList(this.byId("list"));
    },

    bindList(list) {
      list.bindItems({
        path: "/Customers",
        filters: /*e.g.*/this.getInitialFilter(), // dynamic filter(s)
        select: [ // Reduce data load
          "CustomerID",
          "CompanyName",
          "CompanyCity",
          "CompanyCountry",
        ].join(","),
        templateShareable: false,
        template: new StandardListItem({
          title: "{CompanyName}",
          wrapping: true,
          description: "{City}, {Country}",
        }),
      });
    },

    getInitialFilter() {
      return this._initialFilter || (this._initialFilter = new Filter({
        path: "Fax",
        operator: FilterOperator.NE,
        value1: null, // Note: in case of Mockserver, there is a bug when filtering by null. See https://github.com/SAP/openui5/issues/1955
        caseSensitive: false,
      }));
    },

    onSearch(event) {
      this.byId("list").getBinding("items").filter(new Filter({
        filters: [
          this.getInitialFilter(),
          this.getSearchFilters(event.getParameter("query")),
        ],
        and: true,
      }), FilterType.Application);
    },

    getSearchFilters(query) {
      return new Filter({
        filters: [
          new Filter({
            path: "Country",
            operator: FilterOperator.Contains,
            value1: query,
            caseSensitive: false,
          }),
          new Filter({
            path: "City",
            operator: FilterOperator.Contains,
            value1: query,
            caseSensitive: false,
          }),
        ],
        and: false,
      })
    },

    onExit() {
      this._initialFilter?.destroy();
      this._initialFilter = null;
    },

  });
});