Extending a control in the SAPUI5 library: sap.ui.model.json.JSONModel
Extending a control in the SAPUI5 library: sap.ui.model.json.JSONModel
(Written based on version 1.28 of SAPUI5)
On a recent project building SAPUI5 application to be hosted on HCP (HANA Cloud Platform) we had a requirement to interface with RESTful services exposed by PI. Being a Gateway and OData girl myself this was a new challenge. “At least a REST service is better than a SOAP service”, I thought. “But how can I load the data in, in a neat and tidy way, like I would with a OData service?” The answer: extend the standard JSONModel, and bend it to my will!
Requirements
- To be able to load data from a single service into the model without overwriting everything already in it
- The “loadData” function already available in the JSONModel could not be used to fulfil this requirement as it sets the response of the request at the root of the model, overwriting any existing data
- To load data onto a specified path of the model, in order to allow binding in a view to the correct path before the data is loaded in
- To be able to specify whether the structure of the data loaded in should be an array or not
- An oddity of PI, or more likely the underlying services it is communicating with, seems to be that if a request for a set of data only finds a single item, it will return an object, rather than an array with one object in it
- To be able to bind only specific properties of the response to the model
- E.g. the response data is in the following structure, we only want to bind to “Items” node to our model
- “d” : { “results” : { “Items” : [ “a” : ] } }
- E.g. the response data is in the following structure, we only want to bind to “Items” node to our model
Overall, I wanted to make my JSONModel “feel” more like an ODataModel.
Solution
- Extend the JSONModel control
- On instantiation of the model (in the constructor function) include a service URL parameter on which to base all calls subsequently made against the model
- Add a new function within the model for loading the data
- Add functions to the model to allow “create”, “update” and “remove”
In order to write the code around this I referred to the ODataModel code base, and stole relevant chunks where rewriting them would have been unnecessary.
As the code would be useful across many applications I hosted it on HCP as an HTML5 application called “shared”, even though it is simply a collection of files rather than an executable application. In this way I can now access the extended JSONModel code from other applications hosted on the same HCP by adding a new destination, of type “application”, into the neo-app.json file, and adding a new resource root into the index file.
Please see my GitHub repository, sapui5-shared, for the source code of my extended JSONModel.
Example index.html resource root
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8">
<title>Bluefin Application One</title>
<script id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-xx-bindingSyntax="complex"
data-sap-ui-resourceroots='{
" bluefin.application1": "./",
"bluefin.shared": "./shared"
}'>
</script>
<script>
sap.ui.getCore().attachInit(function() {
new sap.ui.core.ComponentContainer({
height : "100%",
name : "bluefin.application1"
}).placeAt("content");
});
</script>
</head>
<body class="sapUiBody" id="content">
</body>
</html>
Example neo-app.json file
{
"welcomeFile": "index.html",
"routes": [
{
"path": "/resources",
"target": {
"type": "service",
"name": "sapui5",
"entryPath": "/resources"
},
"description": "SAPUI5 Resources"
},
{
"path" : "/shared",
"target" : {
"type" : "application",
"name" : "shared"
},
"description" : "Shared Utilities"
},
{
"path": "/Search",
"target": {
"type": "destination",
"name": "myHCPDestination",
"entryPath" : "/Search"
},
"description": "Search UI5 destination"
}
]
}
Example code-snippet for use of bluefin.shared.model.JSONModel.loadDataFromPath
jQuery.sap.require("bluefin.shared.model.JSONModel");
var oMyModel = new bluefin.shared.model.JSONModel("/Search");
// Read the booking data from the service
oMyModel.loadDataFromPath("/Objects/Bookings"
{
sModelPath : "/Bookings",
aResponseProperty : [ "d", "results"],
bReturnArray : true
},
null,
false
);
// Array of data can now be found on the model
var aBookings = oMyModel.getProperty("/Bookings");
bluefin.shared.model.JSONModel
The code for the extended JSON Model can be found in the model folder of my sapui5-shared repository.
Nice !
Hi Lindsay,
Good info and a differet approach to resolve the issue.
I had a question though, What is the use of neo-app.json file?
Good question!
neo-app.json is used in applications which are hosted on HCP (HANA Cloud Platform). It is used to map to destinations on HCP (set-up in the "Destinations" section of HCP which connect to services), resources (the UI5 library) and other applications hosted on the same platform, as other HTML5 applications.
This documentation may be helpful to you to understand better: SAPUI5 SDK - Demo Kit
Thanks 🙂
fyi, loadData has a flag bMerge which can be set to true if the data has to merged instead of override.
oModel.loadData(sUrl,{bMerge:true});
Thanks Sakthivel! I've never used the merge parameter on the JSON model before. Does this mean if you make an entirely different "loadData" request, i.e. to a new path on a new service, with bMerge = true, this data will be added to the JSON model as a new property node?
Thanks for sharing this 🙂 I have learnt something new!
Nope, it calls jQuery.extend() using the existing data and the loaded data.
Nice work. This has proven to be extremely useful.
Nice article, Lindsay!
Thank you for your input.