OData Operations¶
You can perform various operations on OData Entity Set such as retrieving data, binding data to UI controls, create, update, delete entities and call OData functions.
Retrieving Data¶
An entity set collection will be returned in the form of an array that can be bound to a control. You can use Target binding on controls that support it or you can use OData Read action or ClientAPI.read to retrieve the data.
Target Binding¶
You can use Target
binding to bind data to the UI controls such as Object Table, Contact Table, and so on.
A Target
binding typically consist of the following:
Service
- refers to the target OData serviceEntitySet
- refers to the target OData Entity SetQueryOptions
- additional query parameters such as$expand
,$filter
,$orderby
,$top
, and more, that will be appended to the query to the OData service.
For certain container controls such as Object Table, we will enable paging (50 rows per page, by default), if it's the last control in the page. You can adjust the paging with DataPaging
property of the control, if it's available.
You can also set the Target
to a rule and return an array of objects with key-value properties.
Example
Binding an Object Table to the Events
entity set:
{
"ObjectCell": {
"Title": "{Name} - {Type}",
"Subhead": "{Country}",
"Description": "{Description}",
"Footnote": "{@odata.readLink}",
"OnPress": "/EventApp/Actions/Events/NavToEventDetail.action"
},
"Target": {
"EntitySet": "Events",
"QueryOptions": "$orderby=Name",
"Service": "/EventApp/Services/EventManagementService.service"
},
"_Type": "Section.Type.ObjectTable"
}
Binding an Object Table to the Events(123)/Sessions
navigation property:
Assuming your page current binding context is the data of Events(123)
{
"Header": {
"Caption": "Upcoming Session"
},
"ObjectCell": {
"Title": "{Title}",
"Description": "{Description}",
"StatusText": "{Track/Name}",
"Footnote": "Start: $(DT, {StartDateTime})",
},
"Target": {
"EntitySet": "{@odata.readLink}/Sessions", //<-- will resolved to "EntitySet": "Events(123)/Sessions",
"QueryOptions": "$orderby=StartDateTime&$top=3&$expand=Track",
"Service": "/EventApp/Services/EventManagementService.service"
},
"Footer": {
"Caption": "See All Sessions",
"OnPress": "/EventApp/Actions/Sessions/NavToSessions.action"
},
"_Type": "Section.Type.ObjectTable"
}
Read Action & ClientAPI.read¶
You can use OData Read Action and ClientAPI.read
to retrieve data from your OData service for business logic processing
OData Read Action Example¶
{
"OnSuccess": "/EventApp/Rules/ProcessReadResult.js",
"OnFailure": "/EventApp/Rules/TestRule.js",
"Target": {
"EntitySet": "Suppliers",
"Service": "/EventApp/Services/EventManagementService.service"
},
"ActionResult": {
"_Name": "ReadResult"
},
"_Type": "Action.Type.ODataService.Read"
}
export default function ProcessReadResult(clientAPI) {
let res = clientAPI.getActionResult("ReadResult");
let dataObservableArray = res.data;
for (var i = 0; i < dataObservableArray.length; i++) {
let item = dataObservableArray.getItem(0);
// Process each item here...
}
}
ClientAPI
Read Example¶
export default function ProcessReadResult(clientAPI) {
let servicePath = "/EventApp/Services/EventManagementService.service";
let entitySet = "Events";
let propertiesToSelect = ["EventID", "Name", "Description"]; //Optional - set to null to return all properties of the entity
let queryOptions = "$orderby=Name";
return clientAPI.read(servicePath, entitySet, propertiesToSelect, queryOptions).then((dataObservableArray)=> {
for (var i = 0; i < dataObservableArray.length; i++) {
let item = dataObservableArray.getItem(0);
// Process each item here...
}
return null;
});
}
Modifying Data¶
You can create, update and delete OData entities by using the OData Service actions such as CreateEntity
, UpdateEntity
and more.
All OData actions requires Target
, the definition of the target depends on the actions you are performing.
For create entity you must target an entity set e.g. Events
, but for update entity or delete entity, you must target a single specific entity which is targeted via read link e.g. Events(123)
or using a filter that guarantee to return a single entity e.g. $filter=EventID eq 123
Create Entity¶
Example of Create Entity action:
{
"_Type": "Action.Type.ODataService.CreateEntity",
"Properties": {
"Title": "{#Control:TitleSimplePropertyFormCell/#Value}",
"StartDateTime": "{#Control:StartDateTimeFormCell/#Value}",
"EndDateTime": "{#Control:EndDateTimeFormCell/#Value}",
"Description": "{#Control:DescriptionNoteFormCell/#Value}"
},
"Target": {
"EntitySet": "Sessions",
"Service": "/EventApp/Services/EventManagementService.service"
},
"ActionResult": {
"_Name": "CreateSessionResult"
},
"OnSuccess": "/EventApp/Actions/ClosePageAsCompleted.action",
"OnFailure": "/EventApp/Actions/ActionFailedMessage.action"
}
Update Entity¶
Example of Update Entity action (take note of the ReadLink
):
{
"_Type": "Action.Type.ODataService.UpdateEntity",
"Properties": {
"Title": "{#Control:TitleSimplePropertyFormCell/#Value}",
"Description": "{#Control:DescriptionNoteFormCell/#Value}"
},
"Target": {
"EntitySet": "Sessions",
"ReadLink": "{@odata.readLink}",
"Service": "/EventApp/Services/EventManagementService.service"
},
"ActionResult": {
"_Name": "UpdateSessionResult"
},
"OnSuccess": "/EventApp/Actions/ClosePageAsCompleted.action",
"OnFailure": "/EventApp/Actions/ActionFailedMessage.action"
}
Delete Entity¶
Example of Delete Entity action (take note of the ReadLink
and lacks of Properties
):
{
"_Type": "Action.Type.ODataService.DeleteEntity",
"Target": {
"EntitySet": "Sessions",
"ReadLink": "{@odata.readLink}",
"Service": "/EventApp/Services/EventManagementService.service"
},
"ActionResult": {
"_Name": "DeleteSessionResult"
},
"OnSuccess": "/EventApp/Actions/ClosePageAsCompleted.action",
"OnFailure": "/EventApp/Actions/ActionFailedMessage.action"
}
For more examples and information please see the Mobile Development Kit API Reference.
Linking¶
You can create link between item in several ways:
-
Using
CreateLinks
in Create Entity action:In this example, you are creating an Sessions entity that link it to an existing Events entity via Session's "Event" navigation property:
{ "_Type": "Action.Type.ODataService.CreateEntity", "Properties": { "Title": "{#Control:TitleSimplePropertyFormCell/#Value}", "StartDateTime": "{#Control:StartDateTimeFormCell/#Value}", "EndDateTime": "{#Control:EndDateTimeFormCell/#Value}", "Description": "{#Control:DescriptionNoteFormCell/#Value}" }, "Target": { "EntitySet": "Sessions", "Service": "/EventApp/Services/EventManagementService.service" }, "CreateLinks": [ { "Property": "Event", "Target": { "EntitySet": "Events", "ReadLink": "{#Page:EventDetail/#Property:@odata.readLink}", } } ], "ActionResult": { "_Name": "CreateSessionResult" }, "OnSuccess": "/EventApp/Actions/ClosePageAsCompleted.action", "OnFailure": "/EventApp/Actions/ActionFailedMessage.action" }
The above is equivalent of sending 2 POST requests: * 1st POST to create the Sessions Entity * 2nd POST request to link the created Sessions entity to the Events(123)
POST https://my-odata-service.com/Sessions OData-Version: 2.0 Content-Type: application/json;odata.metadata=minimal Accept: application/json { "Title": "Rapid Application Development with Mobile Development Kit", "StartDateTime": "20-10-2020T09:00:00Z", "EndDateTime": "20-10-2020T10:00:00Z", "Description": "Learning to build application rapidly with metadata driven application framework" }
Let's assume the back end will generate ID 111 for the newly created session, hence Sessions(111)
POST https://dummy-odata-service.com/Sessions(111)/Event/$ref OData-Version: 2.0 Content-Type: application/json;odata.metadata=minimal Accept: application/json { "@odata.id": "https://dummy-odata-service.com/Events(123)" }
-
Using
CreateRelatedEntity
action This action allow you to create an entity directly via the navigation property of a related parent entity.Example:
{ "_Type": "Action.Type.ODataService.CreateRelatedEntity", "Properties": { "Title": "{#Control:TitleSimplePropertyFormCell/#Value}", "StartDateTime": "{#Control:StartDateTimeFormCell/#Value}", "EndDateTime": "{#Control:EndDateTimeFormCell/#Value}", "Description": "{#Control:DescriptionNoteFormCell/#Value}" }, "Target": { "EntitySet": "Sessions", "Service": "/EventApp/Services/EventManagementService.service" }, "ParentLink":{ "Property": "Session", "Target": { "EntitySet": "Events", "ReadLink": "{#Page:EventDetail/#Property:@odata.readLink}", // Assumption: EventDetail page's root binding context is an Events entity } }, "ActionResult": { "_Name": "CreateSessionResult" }, "OnSuccess": "/EventApp/Actions/ClosePageAsCompleted.action", "OnFailure": "/EventApp/Actions/ActionFailedMessage.action" }
HTTP Request equivalent of the above example:
Sending a post request against
/Events(123)/Sessions
navigation with payload of a session. This will create a new session and link it toEvents(123)
entity.POST https://dummy-odata-service.com/Events(123)/Sessions OData-Version: 2.0 Content-Type: application/json;odata.metadata=minimal Accept: application/json { "Title": "Rapid Application Development with Mobile Development Kit", "StartDateTime": "20-10-2020T09:00:00Z", "EndDateTime": "20-10-2020T10:00:00Z", "Description": "Learning to build application rapidly with metadata driven application framework" }
-
Deep Insert In this example, you will create an
Events
entity together with an associatedGlobalThemes
entity (in theTheme
navigation property) using deep insert:{ "_Type": "Action.Type.ODataService.CreateEntity", "Properties": { "Name": "{#Control:Name/#Value}", "Description": "{#Control:Description/#Value}", "Type": "{#Control:Type/#Value}", "Country": "{#Control:Country/#Value}", "Theme": { "DisplayName": "{#Control:ThemeDisplayName/#Value}", "ThemeCode": "{#Control:ThemeCode/#Value}" } }, "Target": { "EntitySet": "Events", "Service": "/EventApp/Services/EventManagementService.service" }, "ActionResult": { "_Name": "CreateEventResult" }, "OnSuccess": "/EventApp/Actions/ClosePageAsCompleted.action", "OnFailure": "/EventApp/Actions/ActionFailedMessage.action" }
The above is equivalent of sending a single POST request that will create an
Events
entity and an associatedGlobalThemes
entity in the same request.POST https://my-odata-service.com/Events OData-Version: 2.0 Content-Type: application/json;odata.metadata=minimal Accept: application/json { "Name": "SAP Sapphire NOW", "Description": "Discover how you can transform your business and become an intelligent, sustainable enterprise at SAP Sapphire. Our reimagined event experience is being held as a hybrid series – a virtual event combined with a nine-city, in-person world tour experience.", "Type": "Public", "Country": "United States", "Theme": { "DisplayName": "SAP Horizon", "ThemeCode": "sap_horizon" } }
!!! Note There are some limitations with respect to deep insert in Offline OData. In the Offline OData scenario, deep insert only works for navigation properties with a maximum cardinality of one in the case where the back end cannot respond with child entities in the same order as the request. Please see Limitations with Deep Inserts for more information.
Call Function¶
You can use CallFunction
action or callFunction
API to execute and OData function (also known as Function Import and Action Import)
The OData Version 4 specification gives us a definition what Functions, Actions are:
Functions and Actions are operations that allow the execution of custom logic on parts of a data model. Functions are operations that do not have side effects and may support further composition, for example, with additional filter operations, functions or an action. Actions are operations that allow side effects, such as data modification, and cannot be further composed in order to avoid non-deterministic behavior. Actions and functions are either bound to a type, enabling them to be called as members of an instance of that type, or unbound, in which case they are called as static operations. Action imports and function imports enable unbound actions and functions to be called from the service root.
Example of using CallFunction
action to call a function import
{
"_Type": "Action.Type.ODataService.CallFunction",
"Target": {
"Function": {
"Name": "getSessionAttendeesCount",
"Parameters": {
"SessionID": "{SessionID}"
}
},
"ActionResult" : {
"_Name" : "AttendeesCountResult"
},
"Service": "/EventApp/Services/EventManagementService.service",
"OnSuccess": "/EventApp/Rules/DisplayCount.js",
"OnFailure": "/EventApp/Actions/ActionFailedMessage.action"
}
}
export default function DisplayCount(clientAPI) {
let res = clientAPI.getActionResult("AttendeesCountResult");
//Because this Function Import return an Edm.Int64 type data, the result in res.data will simply be an integer value.
return clientAPI.executeAction({
"Name": "/EventApp/Actions/Message.action",
"Properties": {
"Message": "Attendees Count: " + res.data
}
});
}
Example of using Call Function in the Target binding of an UI control
{
"ObjectCell": {
"Title": "{Title}",
"Subhead": "{Description}",
"StatusText": "{StartDateTime}",
"SubstatusText": "{EndDateTime}"
},
"Target": {
"Service": "/EventApp/Services/EventManagementService.service",
"Function": {
"Name": "getPopularSessions",
"Parameters": {
"EventID": "{EventID}"
}
}
},
"_Name": "PopularSessionTable",
"_Type": "Section.Type.ObjectTable"
}
Example of using callFunction
API to call a function import
export default function GetSessionAttendeesCount(clientAPI) {
let servicePath = "/EventApp/Services/EventManagementService.service";
let functionDef = {
"Name": "getSessionAttendeesCount",
"Parameters": {
"SessionID": "1"
}
};
let headers = null;
return clientAPI.callFunction(servicePath, functionDef, headers).then((result)=>{
//Because this Function Import return an Edm.Int64 type data, the result in result will simply be an integer value.
return clientAPI.executeAction({
"Name": "/EventApp/Actions/Message.action",
"Properties": {
"Message": "Attendees Count: " + result
}
});
});
}