Introduction
I haven’t used the OData v4 model in UI5 Freestyle apps a lot. I had the idea that I would lose the flexibility of the Odata v2 model (in combination with a JSON model) and this would block me at some point developing UI5 apps.
My main concern was the possibility to create a custom OData request to my backend with the OData v4 model. I was not yet aware of the fact that the OData v4 model allows us to create custom OData requests.
The OData v4 model, just like in V2, has the ability to use direct bindings, which I do recommend. This will take care of many things in regards to fetching and sending data to the backend. Nevertheless, this might not always be enough. Sometimes, because of circumstances, it might be needed to create a custom Odata request to your backend. Without this, at some point you will get stuck.
After sharing my concerns about this to the UI5 team, Margot connected me with the development team of the OData v4 model. They pointed me out that this was already possible since version 1.70... Unbelievable that I missed that... The details on how this can be done is available in the UI5 documentation:
https://ui5.sap.com/#/topic/17b30ac2d5474078be31e695e97450cc
I made a demo project to try different examples for sending custom OData requests to the backend using the OData v4 model. As a starting point, I used a similar project that I used as a demo for the OData v2 model wrapper function, shared in the following blog posts:
- TypeScript version:
https://blogs.sap.com/2021/11/30/my-first-experience-with-typescript-in-ui5-odatamodel-service-wrapp... (GitHub:
https://github.com/lemaiwo/TypeScriptServiceDemoApp)
- JavaScript version:
https://blogs.sap.com/2018/09/17/ui5-odata-requests-with-promises/ (GitHub:
https://github.com/lemaiwo/PromiseDemo )
This time I also needed an OData v4 service, the
Northwind v4 service does not comply with the OData v4 standard and does not work. As a workaround, I made a CAP project based on the book sample. In this project, I added a UI5 app using TypeScript as a showcase on how to create custom OData requests using the OData Model v4:
https://github.com/lemaiwo/ODatav4DemoApp
The biggest change (compared to V2) is
located in the service class:
https://github.com/lemaiwo/ODatav4DemoApp/blob/main/app/ODataV4DemoUI/src/service/BookService.ts . (The base class for the service, that I used in the OData Model v2, which includes the wrapper function, is not needed anymore. The main goal of this base class and wrapper function was to wrap every custom http request into a promise.) One of the great things about this
OData v4 model is that it is using promises and not callbacks, which makes the wrapper obsolete.
It is still a good practice to separate custom OData requests in a Service class/object (even without the base class).
Compare OData v2 Model wrapper with OData v4 Model
The "NorthwindService" class in my OData v2 example contains the same kind of requests as the "BookService" class in my OData v4 example. The “BookService” still contains the OData v2 code in its comments.
Odata v2 Model Wrapper example:
https://github.com/lemaiwo/TypeScriptServiceDemoApp/blob/main/src/service/NorthwindService.ts
Odata v4 Model example:
https://github.com/lemaiwo/ODatav4DemoApp/blob/main/app/ODataV4DemoUI/src/service/BookService.ts
Let's start building the OData requests using the OData v4 model starting from an OData v2 example:
Before comparing both, I have added one thing for the OData v4 model in the constructor: "bindList"
Reusing the same binding will reduce the amount requests to the backend. The "$$getKeepAliveContext" allows to reuse the earlier fetched contexts when querying a specific context (by key) and not send a new request to the backend. (For example in "getBookId")
Get entitySet
Getting a list of entities is done by using the function "requestContexts" from the listbinding "bookBinding" initialized in the constructor. It will return a promise with a list of bindingContexts that contains all details of an entity like path, properties, ...:
https://ui5.sap.com/#/api/sap.ui.model.odata.v4.ODataListBinding%23methods/requestContexts
I use the map function to return a list of the objects itself and convert it to the correct type.
https://github.com/lemaiwo/ODatav4DemoApp/blob/e3c4c1bc730a076378fd6f042156a5874ef3024d/app/ODataV4D...
OData v2 model, I use my wrapper which uses the read function:
https://ui5.sap.com/#/api/sap.ui.model.odata.v2.ODataModel%23methods/read
Get entitySet with filter
Getting a filtered list of entities is similar to fetching a full list. One remark, I used a new listbinding instance. If we first get the filtered list and afterwards use the same listbinding for getting the full list, we will still receive the filtered list and not the full list.
- Create a listbinding and immediately apply the filter to it
- Use “requestContexts” on the listbinding to get the result using promises
- Just like the get entityset I use the map function to only get back the objects
https://github.com/lemaiwo/ODatav4DemoApp/blob/e3c4c1bc730a076378fd6f042156a5874ef3024d/app/ODataV4D...
With the OData v2 model, this is also done with the "read" function by adding additional parameters.
Get entity using keys
For getting a specific entity using the keys we have two options:
- bindContext: This will create a binding context for the provided path of the entity including the keys, for example: ‘/Books(<id>)’
- getKeepAliveContext: it will also create a binding context for the provided path of the entity including the keys but it will try to find it in the earlier fetched contexts
The binding context can be used to get the requested object using the function "requestObject".
https://github.com/lemaiwo/ODatav4DemoApp/blob/e3c4c1bc730a076378fd6f042156a5874ef3024d/app/ODataV4D...
OData v2 model: this is also done using the "read" function using the path of the entity including keys
Using sort with skip and top
This next function in my demo app is a bit strange and would normally NOT be done in the UI. I just want to use it as an example for a sort and skip/top.
A sort is similar to a filter, just set the sort to the binding and use "requestContexts". Skip and Top, is passed in the "requestContexts" function as the first and second parameter.
https://github.com/lemaiwo/ODatav4DemoApp/blob/e3c4c1bc730a076378fd6f042156a5874ef3024d/app/ODataV4D...
With the OData v2 model, this is also done with the "read" function by adding additional parameters.
Create entity
Just like for reading data the OData v4 List Binding comes with a function for creating data:
https://ui5.sap.com/#/api/sap.ui.model.odata.v4.ODataListBinding%23methods/create
This function will send a POST request to the backend with the object passed as the first parameter. It will not return a promise but a context for the created entity. The context will not contain any object until the entity is created in the backend. To know the create operation in the backend is finished successfully you have to use the function “created”, which returns a promise. Afterwards, the context can be used to access the created entity data.
https://github.com/lemaiwo/ODatav4DemoApp/blob/e3c4c1bc730a076378fd6f042156a5874ef3024d/app/ODataV4D...
OData v2 model comes with a “create” function directly on the ODataModel itself.
Delete entity
OData v4 model does not have a function directly on the binding for deleting an entity. If you want to delete an entity, you will first have to fetch the context. From the context it is possible to call the delete function which will generate a DELETE request.
https://github.com/lemaiwo/ODatav4DemoApp/blob/e3c4c1bc730a076378fd6f042156a5874ef3024d/app/ODataV4D...
With OData v2 model it is possible to do this directly from the OData model v2.
Updates will be triggered by using the setProperty function on the binding. Depending on the groupid ($auto or api) it will send the update immediately or on submitBatch
https://ui5.sap.com/#/api/sap.ui.model.odata.v4.ODataPropertyBinding%23methods/setValue
More information about custom OData requests with the OData v4 model:
https://ui5.sap.com/#/topic/17b30ac2d5474078be31e695e97450cc
The full demo project is available on GitHub:
https://github.com/lemaiwo/ODatav4DemoApp
You can run the project by first running “npm install” followed by “npm start”.
The idea of this simple application is to provide an example on how to create custom OData requests using the OData v4 model covering different possible scenarios. Starting from simple things like fetching an array of data followed by applying a filter or sort to end up with create and delete.
I know the getBooksNextID and deleteLatestBook are strange functions and would/should never be done this way in a real app. It was just a way to bring in an example for sorting and deleting a book.
Conclusion
We should not hold back to use the OData v4 model! Compared to the OData v2 Model, which we are all familiar with, it might be a big change. On the other hand, it is more consistent compared to how bindings on controls work. For example if you want to apply a filter on a listbinding of control (even in v2) than this uses the same syntax as for a custom OData request in the v4 model.
What I also like is that it using promises instead of callback functions in the v2 model.
Overall the OData model v4 allows you to use OData v4 api’s without any 3th party library to fully benefit of the new functionalities in OData v4.
One more thing, there is a great episode about OData in UI5 on the UI5 NewsCast podcast available. Feel free to listen to this episode here:
https://podcast.opensap.info/ui5-newscast/2022/05/18/ui5-newscast-026-how-to-deal-with-odata-in-ui5/