SAPUI5 and Fiori Tools Part 5: OData V4 Read, Create, Update and Errors
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
- Configure the V4 model (code example).
- Bind the list with $count and a sorter.
- Add Create with oListBinding.create and a "changes" group.
- Add Save (submitBatch) and Cancel (resetChanges).
- Add a MessagePopover button bound to the message model.
✅ CHECKPOINT: your freestyle app reads, creates, updates and reports backend errors properly.
Code example
// ===== 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.
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
- SAPUI5 and Fiori Tools Part 1: MVC, Models, Views and Controllers
- SAPUI5 and Fiori Tools Part 2: Set Up BAS or VS Code and Generate an App
- SAPUI5 and Fiori Tools Part 3: Data Binding — JSON Model, OData Model, Modes
- SAPUI5 and Fiori Tools Part 4: Routing, Navigation, Fragments and Dialogs
- SAPUI5 and Fiori Tools Part 6: Debugging, Testing and Performance
- SAPUI5 and Fiori Tools Part 7: Deployment to BTP and ABAP