Extending SAPUI5 JSON Model
Hi All
Since the first release of SAPUI5 I started trying to use it with JSON and I did not find a way to pass callback functions to the loadData method of the JSON Model. While reading the SAPUI5 documentation I found two methods that can be used to attach handlers to the request completed and request failed events, but to be honest I didn’t find it useful. So I decided to post a thread in the SCN forum.
After few weeks without answers I decided to take a look under the hood. When I opened the JSONModel.js file I saw where the loadData method calls the jQuery.ajax and I finally found what I was looking for, i.e., what I need to change to meet my requirements.
Figure 1 – JSON Model source code
Now that I knew what needed to be changed in the loadData method to received the callback functions as parameters the next question was: how to extend the JSON Model? Turning back to the SAPUI5 documentation I found the extend method which enables, as it’s name says, to extend the JSON Model.
Sharing the results
myJSONModel.js file with the JSON Model extension
How it was extended: a new method was declared (loadDataNew). Two new parameters (fnSuccess and fnError) were added to the method and the callback functions calls were inserted (see source code comments).
jQuery.sap.require("sap.ui.model.json.JSONModel");
sap.ui.model.json.JSONModel.extend("myJSONModel", {
//declare our new method including two new parameters fnSuccess and fnError, our callback functions
loadDataNew: function(sURL, fnSuccess, fnError, oParameters, bAsync, sType, bMerge, bCache){
var that = this;
if (bAsync !== false) {
bAsync = true;
}
if (!sType) {
sType = "GET";
}
if (bCache === undefined) {
bCache = this.bCache;
}
this.fireRequestSent({url : sURL, type : sType, async : bAsync, info : "cache="+bCache+";bMerge=" + bMerge});
jQuery.ajax({
url: sURL,
async: bAsync,
dataType: 'json',
cache: bCache,
data: oParameters,
type: sType,
success: function(oData) {
if (!oData) {
jQuery.sap.log.fatal("The following problem occurred: No data was retrieved by service: " + sURL);
}
that.setData(oData, bMerge);
that.fireRequestCompleted({url : sURL, type : sType, async : bAsync, info : "cache=false;bMerge=" + bMerge});
// call the callback success function if informed
if (typeof fnSuccess === 'function') {
fnSuccess(oData);
}
},
error: function(XMLHttpRequest, textStatus, errorThrown){
jQuery.sap.log.fatal("The following problem occurred: " + textStatus, XMLHttpRequest.responseText + ","
+ XMLHttpRequest.status + "," + XMLHttpRequest.statusText);
that.fireRequestCompleted({url : sURL, type : sType, async : bAsync, info : "cache=false;bMerge=" + bMerge});
that.fireRequestFailed({message : textStatus,
statusCode : XMLHttpRequest.status, statusText : XMLHttpRequest.statusText, responseText : XMLHttpRequest.responseText});
// call the callback error function if informed
if (typeof fnError === 'function') {
fnError({message : textStatus, statusCode : XMLHttpRequest.status, statusText : XMLHttpRequest.statusText, responseText : XMLHttpRequest.responseText});
}
}
});
}
});
and below is the code implemented in the index.html to exemplify its use
<!DOCTYPE html>
<html><head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<title>Extending the JSON Model</title>
<style>
#firstname, #lastname, #btn1, #btn2, #btn3 { margin: 0 4px; }
#content{ margin-top: 10px; }
</style>
<script id='sap-ui-bootstrap' type='text/javascript'
src='../../sapui5/resources/sap-ui-core.js'
data-sap-ui-theme='sap_goldreflection'
data-sap-ui-libs='sap.ui.commons'></script>
<script type='text/javascript' src='myJSONModel.js'></script>
<script>
//callback function for successful requests
function request_success (oData) {
if(oData.success){
sap.ui.commons.MessageBox.alert('Data loaded successfully');
}else{
sap.ui.commons.MessageBox.alert(oData.msg);
}
}
//callback function to handle communication errors
function request_error () {
sap.ui.commons.MessageBox.alert(arguments[0].statusText);
}
//create the ApplicationHeader control
var oAH = new sap.ui.commons.ApplicationHeader('appHeader', {
logoSrc: 'http://www.sap.com/global/images/SAPLogo.gif',
logoText: 'SAP SAPUI5 Library',
displayLogoff: false
});
//create the label for field First name
var oL1 = new sap.ui.commons.Label('l1', {
text: 'First name:'
});
//create the label for field Last name
var oL2 = new sap.ui.commons.Label('l2', {
text: 'Last name:'
});
//create Input field First name
var oTF1 = new sap.ui.commons.TextField('firstname', {
value: '{/data/firstname}'
});
//create Input field Last name
var oTF2 = new sap.ui.commons.TextField('lastname', {
value: '{/data/lastname}'
});
//create our custom model
var oModel = new myJSONModel;
//set our model
oTF1.setModel(oModel);
oTF2.setModel(oModel);
//create button to load data from server
var oBtn1 = new sap.ui.commons.Button('btn1',{
text:'Load data (ok)',
press:function(){
oModel.loadDataNew("data_success.json", request_success, request_error);
}
});
//create button to load data from server
var oBtn2 = new sap.ui.commons.Button('btn2',{
text:'Load data (user not found)',
press:function(){
oModel.loadDataNew("data_error.json", request_success, request_error);
}
});
//create button to load data from server
var oBtn3 = new sap.ui.commons.Button('btn3',{
text:'Load data (file not found error)',
press:function(){
oModel.loadDataNew("filenotfound.json", request_success, request_error);
}
});
//create the layout
var oLayout = new sap.ui.commons.layout.HorizontalLayout("Layout1", {
content: [oL1, oTF1, oL2, oTF2, oBtn1, oBtn2, oBtn3]
});
//place our controls
oAH.placeAt("appheader");
oLayout.placeAt("content");
</script>
</head>
<body class='sapUiBody'>
<div id='appheader'></div>
<div id='content'></div>
</body>
</html>
Screenshots
Figure 2 – Initial screen
Figure 3 – Success callback function called with results
Figure 4 – Success callback function called with no results
Figure 5 – Error callback function called (simulating a communication error)
The eclipse project with the complete source code can be downloaded from the url below.
http://jianelli.com.br/sap/scnblog1.zip
Your feedback is most welcome!
Christian Jianelli
Dear Christian,
Why don't you set the standard SAP event on the model.
var oModel = new myJSONModel;
oModel.fireRequestCompleted(
function () {
alert("You Request completed");
}
);
Kind regards,
Onno Bos
Hi Onno,
The documentation says that this method fires the event to attached listeners and the purpose of the extension is to provide an easy way to attach listeners, not fire the events.
Best regards,
Christian
Dear Christian,
Sorry. I meant "attachRequestCompleted". This works perfectly.
Best regards,
Onno
Hi Onno,
As said in the first paragraph I tried to use the attachRequestCompleted and the attachRequestFailed but i didn't find it useful. One of the reasons is that the request failed event also calls the request completed listener, but without any information indicating that an error ocurred and it is called before the request failed listener.
Best regards,
Christian
Hi Christian,
while I was working on my Application I noticed the same thing about the request failed and request completed listener.
Do you know, if the calling of the request completed listener before the request failed listener is considered a bug or was it intentionally made this way?
You said above that this was only one of the reasons why you didn't find it useful. What are the other reasons ?
Kind regards,
Jan
Hi Jan,
I'm not sure if it is a bug or not. I think it's not.
If you compare the JSONModel with the oDataModel you will see that the oDataModel have a "change control" and many other features that the JSONModel does not have. That is the main reason why I think it is not very useful.
Regards,
Christian
Thanks, I'll have a look at it.
Regards,
Jan