Skip to content

Selection Screen ​

A filter form over a CDS entity: a few inputs, a Go button, a result table. The classic internal-tool shape, and the one cap2UI5 is best at.

Built from tested parts, assembled here

Every piece below — bound fields, t.table, cds.ql, the render branch — is exercised in the repository's test suite. This particular assembly is illustrative rather than a file you will find there; the shipped app it is closest to is books.js.

js
// srv/apps/orders.js
import cds from "@sap/cds";
import { defineApp, t } from "@cap2ui5/cds-plugin";
const { SELECT } = cds.ql;

defineApp("ZCL_ORDERS", class {
  customer = "";
  min_total = 0;
  only_open = false;
  rows     = t.table({ ID: 0, customer: "", total: t.packed(11, 2), open: false });
  hits     = 0;

  async main(client) {
    if (client.check_on_navigated()) {
      client.view_display(
        `<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:f="sap.ui.layout.form"` +
        ` displayBlock="true" height="100%">` +
        `<Shell><Page title="Orders">` +

        `<f:SimpleForm editable="true" layout="ResponsiveGridLayout">` +
        `<f:content>` +
        `<Label text="Customer"/><Input value="${client._bind("customer")}"/>` +
        `<Label text="Minimum total"/><Input value="${client._bind("min_total")}"/>` +
        `<Label text="Open only"/><CheckBox selected="${client._bind("only_open")}"/>` +
        `</f:content></f:SimpleForm>` +

        `<Button text="Go" type="Emphasized" press="${client._event("GO")}"/>` +
        `<Text text="${client._bind("hits")} hits"/>` +

        `<Table items="${client._bind("rows")}">` +
        `<columns><Column><Text text="Customer"/></Column>` +
        `<Column><Text text="Total"/></Column><Column><Text text="Open"/></Column></columns>` +
        `<items><ColumnListItem><cells>` +
        `<Text text="{CUSTOMER}"/><ObjectNumber number="{TOTAL}"/><CheckBox selected="{OPEN}" editable="false"/>` +
        `</cells></ColumnListItem></items></Table>` +

        `</Page></Shell></mvc:View>`);
      return;
    }

    if (client.check_on_event("GO")) {
      const { Orders } = cds.entities("my.shop");
      let q = SELECT.from(Orders).where`customer like ${"%" + this.customer + "%"}`;
      if (this.min_total) q = q.and`total >= ${this.min_total}`;
      if (this.only_open) q = q.and`open = ${true}`;

      this.rows = await q;
      this.hits = this.rows.length;
      client.message_toast_display(`${this.hits} orders`);
    }
  }
});

Why GO does not render ​

client.check_on_navigated() is true on the first roundtrip and when the app gets the screen back — not on the GO roundtrip. The view stays as it is, and the new rows and hits are pushed into it on their own: changed bound data needs no client.view_display(). Render again only when the view's structure changes.

The types the form needs ​

fielddeclared aswhy
customer""string
min_total0integer. A decimal threshold would be t.packed(11, 2)
only_openfalseabap_bool; your code still sees true/false
rowst.table({…})an empty array carries no type

A CheckBox binds selected, an Input binds value — ordinary UI5.

Building the query conditionally ​

cds.ql composes, so the filter is plain JavaScript: the customer condition is always there (an empty one is like '%%', which matches every row), and an and is added only for the other fields the user filled. The ${…} holes are bound parameters, so a customer name containing a quote is a value and not a syntax error.

Keeping the criteria ​

They persist for free. customer, min_total and only_open are declared fields, so they are in the draft: come back to the app after a navigation and the form is still filled in — including after a server restart.

Next ​

Released under the MIT License.