Skip to content

External OData Service

cap2UI5 apps have full access to the CAP connection pool. You can consume any external OData service — the call goes inside your main() via the normal cds.connect.to(...) API.

Configuration

In package.json:

json
{
  "cds": {
    "requires": {
      "northwind": {
        "kind": "odata-v2",
        "model": "srv/external/northwind",
        "credentials": {
          "url": "https://services.odata.org/V2/Northwind/Northwind.svc/"
        }
      }
    }
  }
}

The CSN model is generated as usual with cds import https://services.odata.org/V2/Northwind/Northwind.svc/ and saved at srv/external/northwind.csn.

App code

js
// srv/app/read_odata.js
const cds                       = require("@sap/cds");
const z2ui5_if_app              = require("abap2UI5/z2ui5_if_app");
const z2ui5_cl_ui5_view_builder = require("abap2UI5/z2ui5_cl_ui5_view_builder");

class read_odata extends z2ui5_if_app {

  customers = [];

  async main(client) {

    if (client.check_on_init()) {
      const northwind = await cds.connect.to("northwind");
      this.customers  = await northwind.run(SELECT.from("Customers").limit(50));

      const view = z2ui5_cl_ui5_view_builder.factory()
        .ele({ n: `View`, ns: `mvc` })
        .a({ n: `xmlns`,     v: `sap.m` })
        .a({ n: `xmlns:mvc`, v: `sap.ui.core.mvc` });

      const tab = view.ele(`Shell`).ele(`Page`)
        .a({ n: `title`, v: `Northwind - Customers` })
        .ele(`Table`)
        .a({ n: `items`, v: client._bind_edit(this.customers) });

      const cols = tab.ele(`columns`);
      cols.ele(`Column`).tag(`Text`).a({ n: `text`, v: `CompanyName` });
      cols.ele(`Column`).tag(`Text`).a({ n: `text`, v: `ContactName` });
      cols.ele(`Column`).tag(`Text`).a({ n: `text`, v: `Country` });

      tab.ele(`items`).ele(`ColumnListItem`).ele(`cells`)
        .tag(`Input`).a({ n: `value`, v: `{COMPANYNAME}` }).a({ n: `enabled`, b: true })
        .tag(`Input`).a({ n: `value`, v: `{CONTACTNAME}` }).a({ n: `enabled`, b: true })
        .tag(`Text`).a({ n: `text`, v: `{COUNTRY}` });

      client.view_display(view.stringify());

    } else if (client.check_on_event("SAVE_BACK")) {
      const northwind = await cds.connect.to("northwind");
      // … this.customers contains the user edits thanks to two-way binding
      // … run an UPDATE on the service for each row
    }
  }
}

module.exports = read_odata;

What's new here

1. CDS connection inline

js
const northwind = await cds.connect.to("northwind");
this.customers  = await northwind.run(SELECT.from("Customers").limit(50));

The same code you would write in a srv/z2ui5-service.js handler. Anything Node.js allows is allowed inside main() — fetch, OData, file system, Redis, MQ.

2. Two-way on a list

js
const tab = page.ele(`Table`).a({ n: `items`, v: client._bind_edit(this.customers) });

tab.ele(`items`).ele(`ColumnListItem`).ele(`cells`)
  .tag(`Input`).a({ n: `value`, v: `{COMPANYNAME}` }).a({ n: `enabled`, b: true });

Because the array is two-way bound via _bind_edit, UI5 writes user edits on every item property back into the XX delta. On the next roundtrip this.customers holds the modified state. The item-relative paths are uppercase ({COMPANYNAME}, not {CompanyName}): the model is written with uppercase names and mapped back onto your properties case-insensitively.

3. Persistence caveat

The entire this.customers array is serialized and deserialized between roundtrips. With 50 rows: no problem. With 50,000 rows: noticeable. Rules of thumb:

  • Reload fresh in check_on_init() instead of holding the array as long-lived app state.
  • For read-heavy lists with sort/filter/paging: the set_odata_model pattern (see Events) — then the array lives in the frontend driver, not the app instance.

Example with an OData-backed table:

js
async main(client) {
  if (client.check_on_init()) {
    client.set_odata_model("/odata/v4/admin/NorthwindCustomers");

    const view = z2ui5_cl_ui5_view_builder.factory()
      .ele({ n: `View`, ns: `mvc` })
      .a({ n: `xmlns`,     v: `sap.m` })
      .a({ n: `xmlns:mvc`, v: `sap.ui.core.mvc` });

    view.ele(`Shell`).ele(`Page`).a({ n: `title`, v: `Customers (OData)` })
      .ele(`Table`)
      .a({ n: `items`, v: `{/NorthwindCustomers}` })
      // ↑ no _bind_edit, but a static OData path binding
      .ele(`columns`).ele(`Column`).tag(`Text`).a({ n: `text`, v: `Company` });

    client.view_display(view.stringify());
  }
}

You then need your own CAP service action NorthwindCustomers in z2ui5-service.js:

js
srv.on("READ", "NorthwindCustomers", async (req) => {
  const northwind = await cds.connect.to("northwind");
  return northwind.run(req.query);
});

→ Continue with Static XML View.

Released under the MIT License.