SAPUI5 and SAP Fiori Tools
RAP / CDS / ODataintermediate

SAPUI5 and Fiori Tools Part 5: OData V4 Read, Create, Update and Errors

Key takeaway

Work with a RAP OData V4 service from SAPUI5: configure the V4 model, read lists and single entities with $select, create with list bindings, update with two-way binding, submit batch groups and handle errors and messages.

Explanation

📌 KEY POINTS
✅ The OData V4 model is configured in manifest.json as a data source + default model.

✅ Read via bindings (list binding, element binding) — not manual requests.

✅ Create with oListBinding.create(); update with two-way binding; send with submitBatch for deferred groups.

⚠️ Errors come back as messages — show them via the message model, don't swallow them.

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Part 3 introduced binding. Now we build the data layer of a freestyle app on the RAP service ZAPI_PURCHREQ (V4, no draft) — or a UI service if your app uses draft.

━━━━━━━━━━━━━━━━━━━━
▶ 1. CONFIGURE THE MODEL

manifest.json:

  • "sap.app" → dataSources → mainService with uri (service root) and type "OData", odataVersion "4.0".
  • "sap.ui5" → models → "" (default) with dataSource "mainService" and settings (e.g. operationMode: "Server", autoExpandSelect: true, updateGroupId).

autoExpandSelect builds $select/$expand from your bindings automatically — a big performance win.\n\n━━━━━━━━━━━━━━━━━━━━\n▶ 2. READ\n• List: <Table items="{path: '/PurchaseRequest', parameters: {$orderby: 'RequestID desc', $count: true}}">.

  • Single: view.bindElement("/PurchaseRequest(RequestUUID=…)").
  • Filtering/sorting: apply sap.ui.model.Filter/Sorter to the list binding — executed on the server.

━━━━━━━━━━━━━━━━━━━━
▶ 3. CREATE

const oContext = oListBinding.create({ Description: "Laptops", CurrencyCode: "EUR" });

oContext.created().then(success, error) — resolves after the backend confirms.

With a deferred update group, nothing is sent until submitBatch.

━━━━━━━━━━━━━━━━━━━━
▶ 4. UPDATE

With two-way binding (default for V4 inputs), changing an Input updates the context property. With an update group set to "$auto" it is sent automatically; with a named group (e.g. "changes") you call oModel.submitBatch("changes") on Save, and oModel.resetChanges("changes") on Cancel.

━━━━━━━━━━━━━━━━━━━━
▶ 5. DELETE

oContext.delete() — returns a promise.

━━━━━━━━━━━━━━━━━━━━
▶ 6. ACTIONS

const oOp = oModel.bindContext("com.sap.gateway.srvd.zapi_purchreq.v0001.approve(...)", oContext);

oOp.execute().then(…) — use the exact namespaced name from $metadata.

━━━━━━━━━━━━━━━━━━━━
▶ 7. ERRORS AND MESSAGES

  • Backend messages (REPORTED) arrive in the message model (sap.ui.core.Messaging / MessageManager, depending on UI5 version).
  • Bind a MessagePopover or MessageView to show them; field-related messages appear on the input via target.
  • In promise catch handlers, avoid generic alerts; let messages surface.
  • 412 (ETag conflict): the model reports it; offer a refresh.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Configure the V4 model (code example).
  2. Bind the list with $count and a sorter.
  3. Add Create with oListBinding.create and a "changes" group.
  4. Add Save (submitBatch) and Cancel (resetChanges).
  5. Add a MessagePopover button bound to the message model.

✅ CHECKPOINT: your freestyle app reads, creates, updates and reports backend errors properly.

Code example

ABAP Code
// ===== Step 1: manifest.json ====="sap.app": {  "dataSources": {    "mainService": {      "uri": "/sap/opu/odata4/sap/zapi_purchreq_o4/srvd_a2x/sap/zapi_purchreq/0001/",      "type": "OData",      "settings": { "odataVersion": "4.0" }    }  }},"sap.ui5": {  "models": {    "": {      "dataSource": "mainService",      "settings": {        "operationMode": "Server",        "autoExpandSelect": true,          // builds $select/$expand from bindings        "updateGroupId": "changes"         // deferred: send on submitBatch      }    }  }} <!-- ===== Step 2: list binding (List.view.xml) ===== --><Table id="requestTable"       items="{ path: '/PurchaseRequest',                parameters: { $count: true, $orderby: 'RequestID desc' } }">  <columns><Column><Text text="Request" /></Column><Column><Text text="Amount" /></Column></columns>  <ColumnListItem>    <Text text="{RequestID}" />    <ObjectNumber number="{TotalAmount}" unit="{CurrencyCode}" />  </ColumnListItem></Table> // ===== Steps 3-4: create, save, cancel (List.controller.js) =====onCreate: function () {  const oBinding = this.byId("requestTable").getBinding("items");  const oContext = oBinding.create({ Description: "Laptops", CurrencyCode: "EUR" });  oContext.created().then(    () => sap.m.MessageToast.show("Request created"),    () => { /* errors are in the message model */ }  );},onSave: function () {  this.getView().getModel().submitBatch("changes");     // send all pending changes},onCancel: function () {  this.getView().getModel().resetChanges("changes");    // discard pending changes}, // ===== Action call (approve) =====onApprove: function () {  const oContext = this.getView().getBindingContext();  const oAction = this.getView().getModel().bindContext(    "com.sap.gateway.srvd.zapi_purchreq.v0001.approve(...)", oContext);   // name from $metadata  oAction.execute("changes").then(() => sap.m.MessageToast.show("Approved"));  this.getView().getModel().submitBatch("changes");}, // ===== Step 5: messages (bind a MessagePopover to the message model) =====// onInit:  this.getView().setModel(sap.ui.getCore().getMessageManager().getMessageModel(), "message");//          (newer UI5: sap/ui/core/Messaging.getMessageModel())

Real project scenario

A freestyle app used jQuery.ajax calls to the OData service, built URLs manually and showed "Error" alerts for every failure. Performance was poor because every call fetched all fields, and users never saw the actual validation messages from RAP.
Rewritten with the OData V4 model, autoExpandSelect and list/element bindings, requests shrank to the needed fields. Saves used a deferred group with submitBatch, and a MessagePopover showed RAP messages next to the right fields. Code size dropped by a third and support tickets about "unknown errors" disappeared.

Common mistakes

  • Manual AJAX calls instead of the OData V4 model.
  • Not using autoExpandSelect, transferring all fields.
  • Forgetting submitBatch with deferred groups ("my change is not saved").
  • No resetChanges on cancel.
  • Guessing action names instead of reading $metadata.
  • Swallowing errors instead of showing the message model.

Best practices

  • Configure the V4 model in manifest.json with autoExpandSelect.
  • Use bindings for all reads; server-side filtering and sorting.
  • Use a named update group with explicit Save/Cancel.
  • Execute actions with bindContext and the namespaced name.
  • Show backend messages via a MessagePopover.

Interview angle

Questions: "How do you create and update data with the OData V4 model?", "What is an update group?", "How do you handle backend errors?"
Strong answer: configure the V4 model in manifest.json (autoExpandSelect, operationMode Server); read via list/element bindings; create via oListBinding.create and wait for created(); update via two-way binding; deferred update groups are sent with submitBatch and reverted with resetChanges; actions via bindContext(...).execute(); backend messages arrive in the message model and are shown in a MessagePopover.

Practice this lesson

Quick check

1. How do you create a new entity with the OData V4 model?

2. Which error handling practice hides RAP validation messages from users?

3. With a deferred update group in the OData V4 model, when are changes sent?

Interview questions

1. Users say "my change is not saved" in a freestyle V4 app, but no error is shown. What do you check?

Answer it in your own words first, then compare.

2. How do you create, update and save data with the OData V4 model in a freestyle app?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics