Introduction -
For long time I was thinking to build SAPUI5 application based on JSON data. Lots of websites hosts web services or APIs to provide the useful data in the form of JSON format.
Below are the few websites which are exposing the data in the form of JSON APIs.
Website | JSON API / Web Service Documentation link | API / Access Key, OAuth Required |
---|---|---|
Open Weather Map | No Access Key Required | |
Weather Under Ground | API Key Required | |
Rotten Tomatoes | API Key Required | |
GeoNames | API Key Required | |
API Key Required, OAuth Access Required | ||
API Key Required, OAuth Access Required | ||
You Tube | https://developers.google.com/youtube/2.0/developers_guide_json | API Key Required, OAuth Access Required |
API Key Required, OAuth Access Required | ||
Google Maps | https://developers.google.com/maps/documentation/webservices/ | No Access Key Required |
There are lots of blog available which explains how to consume and build SAPUI5 application based on OData but not much information is available on consuming JSON data and hence I thought to write this blog.
By consuming easily available JSON data, we can develop “n” number of mashup web applications. It could be hybrid applications based on public JSON data along with SAP business suite data!
Another advantage I found with this approach is, it makes learning SAPUI5 very easy as you need not to depend on any SAP system for data, access etc. you can get pretty neat data to be consumed in your application and you can play around with that data by experimenting various UI controls.
In this blog, I will walk you through 4 SAPUI5 applications developed by me and based on JSON APIs.
- SAPUI5 Application based on Weather Underground JSON API
- SAPUI5 Application based on GeoNames wikipediaSearch JSON API
- SAPUI5 Application based on Rotten Tomatoes JSON API
- SAPUI5 Application based on Open Weather Map JSON API
Let’s get into details of each application
1. SAPUI5 Application based on Weather Underground JSON API
For this application, I used below JSON API Service URL.
http://api.wunderground.com/api/Your_Key/conditions/forecast/q/autoip.json
To use these APIs, you need to first sign up and get your API key.
This API basically detects your location based on IP address and gets the weather information for your current location. You can still use other APIs to which you need to provide query parameters as per API documentation.
Very important point is you cannot directly access the URL in your SAPUI5 application because you will be making call in the cross domain environment and you may get below kind of errors.
Origin http://localhost:54788 is not allowed by Access-Control-Allow-Origin.
XMLHttpRequest cannot load http://api.example.com/api/xyz.json. Origin http://localhost:54788 is not allowed by Access-Control-Allow-Origin.
To make call without any error, we need to use cross domain Ajax request. Very well explanation on how to do it can be found in blog http://scn.sap.com/community/developer-center/front-end/blog/2013/06/14/useful-jquery-functions-for-sapui5-development
Also you can refer http://scn.sap.com/community/developer-center/front-end/blog/2013/07/15/secret-behind-jsonp
Once you get the JSON data successfully, the next important step is to set this data to JSONModel.
Finally you need to understand data model of JSON format and how you will be able to read it, loop it etc.
You can refer SAPUI5 documentation at below link which very well explains the methods available to manipulate JSON data. https://sapui5.hana.ondemand.com/sdk/#docs/api/symbols/sap.ui.model.json.JSONModel.html
The best approach to see the structure of JSON data is to use online JSON viewer tools. One of the best tools I am using is available athttp://jsonviewer.stack.hu/
Here you need to load JSON raw data by providing direct URL
Once the data is loaded, it will be displayed in raw format.
By clicking on Viewer, it will display JSON data into tree format with name/value pair, deep array, nested objects etc.
Let's see the complete code of the application
<!DOCTYPE html><html><head><!-- Added charset='utf-8' to handle data in various langauges --><meta http-equiv='X-UA-Compatible' content='IE=edge' charset='utf-8' /><title>Weather Forecast</title><!-- Load UI5, select gold reflection theme and the "commons" and "table" control libraries --><script id='sap-ui-bootstrap' type='text/javascript' src='resources/sap-ui-core.js' data-sap-ui-theme='sap_platinum' data-sap-ui-libs='sap.ui.commons,sap.ui.table'></script><script type="text/javascript"> // create a JSONModel, fill in the data and bind the Table to this model var oModel = new sap.ui.model.json.JSONModel(); // service url for Weather Underground JSON API var url = 'http://api.wunderground.com/api/Your_Key/conditions/forecast/q/autoip.json?callback=getJSON'; // var url = 'http://api.wunderground.com/api/Your_Key/conditions/forecast/lang:MR/q/autoip.json?callback=getJSON'; //Example URL // var url = 'http://api.wunderground.com/api/Your_Key/forecast/conditions/lang:MR/q/France/Paris.json?callback=getJSON'; //Example URL //Ajax Call with Callback function and JSONP data type $ .ajax({ url : url, jsonpCallback : 'getJSON', contentType : "application/json", dataType : 'jsonp', success : function(data, textStatus, jqXHR) { oModel.setData(data); sap.ui.getCore().setModel(oModel); // create matrix layout var oMatrix = new sap.ui.commons.layout.MatrixLayout({ id : 'matrix', layoutFixed : false, columns : 3, }); var oCell = new sap.ui.commons.layout.MatrixLayoutCell({ colSpan : 3 }); var oTV = new sap.ui.commons.TextView( { id : 'TV-Head', text : 'SAPUI5 Application based on Weather Underground JSON API', design : sap.ui.commons.TextViewDesign.H1 }); oCell.addContent(oTV); oMatrix.createRow(oCell); //Create a standard divider var oCell = new sap.ui.commons.layout.MatrixLayoutCell({ colSpan : 3 }); oCell.addContent(new sap.ui.commons.HorizontalDivider()); oMatrix.createRow(oCell); //Lable and TextField for City oLabelCity = new sap.ui.commons.Label({ id : 'L-City', text : 'City' }); oTFCity = new sap.ui.commons.TextField({ id : 'TF-City', tooltip : 'City', editable : false, value : '{/current_observation/display_location/full}', width : '200px' }); oLabelCity.setLabelFor(oTFCity); oMatrix.createRow(oLabelCity, oTFCity); //Lable and TextField for Latitute oLabelLat = new sap.ui.commons.Label({ id : 'L-Lat', text : 'Latitude' }); oTFLat = new sap.ui.commons.TextField( { id : 'TF-Lat', tooltip : 'Latitude', editable : false, value : '{/current_observation/display_location/latitude}', width : '200px' }); oLabelLat.setLabelFor(oTFLat); oMatrix.createRow(oLabelLat, oTFLat); //Lable and TextField for longitude oLabelLon = new sap.ui.commons.Label({ id : 'L-Lon', text : 'Longitude' }); oTFLon = new sap.ui.commons.TextField( { id : 'TF-Lon', tooltip : 'Longitude', editable : false, value : '{/current_observation/display_location/longitude}', width : '200px' }); oLabelLon.setLabelFor(oTFLon); oMatrix.createRow(oLabelLon, oTFLon); //Lable and TextField for Elevation oLabelElev = new sap.ui.commons.Label({ id : 'L-Elev', text : 'Elevation' }); oTFElev = new sap.ui.commons.TextField( { id : 'TF-Elev', tooltip : 'Elevation', editable : false, value : '{/current_observation/observation_location/elevation}', width : '200px' }); oLabelElev.setLabelFor(oTFElev); oMatrix.createRow(oLabelElev, oTFElev); //Create a standard divider var oCell = new sap.ui.commons.layout.MatrixLayoutCell({ colSpan : 3 }); oCell.addContent(new sap.ui.commons.HorizontalDivider()); oMatrix.createRow(oCell); //Weather image var oImageWeather = new sap.ui.commons.Image({ src : '{/current_observation/icon_url}', alt : '{/current_observation/icon}' }); //Lable and TextField for weather oLabelWeather = new sap.ui.commons.Label({ id : 'L-Weather', text : 'Weather' }); oTFWeather = new sap.ui.commons.TextField({ id : 'TF-Weather', tooltip : 'Weather', editable : false, value : '{/current_observation/weather}', width : '200px' }); oLabelWeather.setLabelFor(oTFWeather); oMatrix.createRow(oLabelWeather, oTFWeather, oImageWeather); //Create a standard divider var oCell = new sap.ui.commons.layout.MatrixLayoutCell({ colSpan : 3 }); oCell.addContent(new sap.ui.commons.HorizontalDivider()); oMatrix.createRow(oCell); //Lable and TextField for temp_c oLabelTemp = new sap.ui.commons.Label({ id : 'L-Temp', text : 'Temperature' }); var tempstring = oModel .getProperty("/current_observation/temp_c"); //Append Degree Celsius unit symbol to Temperature reading var tempinC = tempstring + "\u2103"; oTFTemp = new sap.ui.commons.TextField({ id : 'TF-Temp', tooltip : 'Temperature', editable : false, value : tempinC, width : '220px' }); oLabelTemp.setLabelFor(oTFTemp); oMatrix.createRow(oLabelTemp, oTFTemp); //Lable and TextField for Obervation Time oLabelObsTime = new sap.ui.commons.Label({ id : 'L-ObsTime', text : 'Observation Time' }); oTFObsTime = new sap.ui.commons.TextField({ id : 'TF-ObsTime', tooltip : 'Observation Time', editable : false, value : '{/current_observation/observation_time}', width : '220px' }); oLabelObsTime.setLabelFor(oTFObsTime); oMatrix.createRow(oLabelObsTime, oTFObsTime); //Lable and TextField for Local Time oLabelLclTime = new sap.ui.commons.Label({ id : 'L-LclTime', text : 'Local Time' }); oTFLclTime = new sap.ui.commons.TextField({ id : 'TF-LclTime', tooltip : 'Local Time', editable : false, value : '{/current_observation/local_time_rfc822}', width : '220px' }); oLabelLclTime.setLabelFor(oTFLclTime); oMatrix.createRow(oLabelLclTime, oTFLclTime); //Lable and TextField for relative humidity oLabelRelHum = new sap.ui.commons.Label({ id : 'L-RelHum', text : 'Relative Humidity' }); oTFRelHum = new sap.ui.commons.TextField({ id : 'TF-RelHum', tooltip : 'Relative Humidity', editable : false, value : '{/current_observation/relative_humidity}', width : '220px' }); oLabelRelHum.setLabelFor(oTFRelHum); oMatrix.createRow(oLabelRelHum, oTFRelHum); //Lable and TextField for Wind oLabelWind = new sap.ui.commons.Label({ id : 'L-Wind', text : 'Wind' }); oTFWind = new sap.ui.commons.TextField({ id : 'TF-Wind', tooltip : 'Wind', editable : false, value : '{/current_observation/wind_string}', width : '220px' }); oLabelWind.setLabelFor(oTFWind); oMatrix.createRow(oLabelWind, oTFWind); //attach it to some element in the page oMatrix.placeAt('content'); //Create an instance of the table control var oTable1 = new sap.ui.table.Table({ title : "Simple Forecast Details", visibleRowCount : 4, selectionMode : sap.ui.table.SelectionMode.Single, navigationMode : sap.ui.table.NavigationMode.Paginator, }); //Define the columns and the control templates to be used oTable1.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Period" }), template : new sap.ui.commons.TextView().bindProperty( "text", "date/weekday"), width : "10px" })); oTable1.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Image" }), template : new sap.ui.commons.Image().bindProperty( "src", "icon_url"), width : "10px", hAlign : "Center" })); oTable1.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "High" }), template : new sap.ui.commons.TextView().bindProperty( "text", "high/celsius"), width : "10px" })); oTable1.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Low" }), template : new sap.ui.commons.TextView().bindProperty( "text", "low/celsius"), width : "10px" })); oTable1.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Avarage Humidity" }), template : new sap.ui.commons.TextView().bindProperty( "text", "avehumidity"), width : "10px" })); oTable1.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Max Humidity" }), template : new sap.ui.commons.TextView().bindProperty( "text", "maxhumidity"), width : "10px" })); oTable1.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Min Humidity" }), template : new sap.ui.commons.TextView().bindProperty( "text", "minhumidity"), width : "10px" })); oTable1.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Chance of Precipitation" }), template : new sap.ui.commons.TextView().bindProperty( "text", "pop"), width : "10px" })); //Create a model and bind the table rows to this model var oModel2 = new sap.ui.model.json.JSONModel(); //Get he forecastday array from simpleforecast object var aSimpleForecast = oModel.getProperty("/forecast/simpleforecast/forecastday"); oModel2.setData({ modelData : aSimpleForecast }); oTable1.setModel(oModel2); oTable1.bindRows("/modelData"); oTable1.placeAt('content'); //Create an instance of the table control var oTable = new sap.ui.table.Table({ title : "Textual Forecast Details", visibleRowCount : 8, selectionMode : sap.ui.table.SelectionMode.Single, navigationMode : sap.ui.table.NavigationMode.Paginator, }); //Define the columns and the control templates to be used oTable.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Period" }), template : new sap.ui.commons.TextView().bindProperty( "text", "title"), width : "50px" })); oTable.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Image" }), template : new sap.ui.commons.Image().bindProperty( "src", "icon_url"), width : "75px", hAlign : "Center" })); oTable.addColumn(new sap.ui.table.Column({ label : new sap.ui.commons.Label({ text : "Forecast" }), template : new sap.ui.commons.TextView().bindProperty( "text", "fcttext_metric"), width : "300px" })); //Create a model and bind the table rows to this model var oModel1 = new sap.ui.model.json.JSONModel(); //Get the forecastday array table from txt_forecast object var aData = oModel.getProperty("/forecast/txt_forecast/forecastday"); oModel1.setData({ modelData : aData }); oTable.setModel(oModel1); oTable.bindRows("/modelData"); oTable.placeAt('content'); } });</script></head><body class='sapUiBody'> <div id='content'></div> <p></p> <div id="footer"> <div style="float: right; text-align: right;"> <img src="http://icons-ak.wxug.com/graphics/wu2/logo_130x80.png" border="0" alt=""><br> <span style="padding-top: 20px;">Weather data provided by Weather Underground, Inc.</span> </div> </div></body></html>
Now let me show you the output screen after running SAPUI5 application. Based on geolocation determined by IP address, it will display weather information.
Yes currently its rainy season here in Pune!
I created view with few textfields and two table UI controls. After getting data from JSON API, I am mapping these fields to textfields and tables.
Let’s understand the code in detail,
Below code makes Ajax call with datatype as JSONP to external JSON APIs. JSON data will be then set to oModel using setData method.
// create a JSONModel, fill in the data and bind the Table to this model
var oModel = new sap.ui.model.json.JSONModel();
// service url for Weather Underground JSON API
var url = 'http://api.wunderground.com/api/Your_Key/conditions/forecast/q/autoip.json?callback=getJSON';
//Ajax Call with Callback function and JSONP data type
$
.ajax({
url : url,
jsonpCallback : 'getJSON',
contentType : "application/json",
dataType : 'jsonp',
success : function(data, textStatus, jqXHR) {
oModel.setData(data);
sap.ui.getCore().setModel(oModel);
});
To set this data to various UI controls, we need to access value of the required JSON object by providing its access path.
For e.g. we want to display city name. As per JSON viewer, city full name can be accessed from path current_observation-->display_location-->full
And on similar lines, to set the value to UI control we need to use {/current_observation/display_location/full}
//Lable and TextField for City
oLabelCity = new sap.ui.commons.Label({
id : 'L-City',
text : 'City'
});
oTFCity = new sap.ui.commons.TextField({
id : 'TF-City',
tooltip : 'City',
editable : false,
value : '{/current_observation/display_location/full}',
width : '200px'
});
oLabelCity.setLabelFor(oTFCity);
oMatrix.createRow(oLabelCity, oTFCity);
If we want to manipulate data before setting it to UI control, we need to get the value using method getProperty of JSON model.
In the application, we want to display temperature with ºC symbol. For that purpose, first we need to get the temperature value and then append symbol to it.
var tempstring = oModel.getProperty("/current_observation/temp_c");
//Append Degree Celsius unit symbol to Temperature reading
var tempinC = tempstring + "\u2103";
The next important piece of code is to display nested array data into table UI control.
In the example application, we are displaying 2 tables with weather information. One of the tables contains Textual Forecast details which is accessible as forecast-->txt_forecast-->forecastday. Here forecastday is the array of objects which we want to display as table data.
As mentioned in below code, we need to first get the access using getProperty method and then by setting it to new JSON model using method setData, we can directly bind the array to the table as model.
//Create a model and bind the table rows to this model
var oModel1 = new sap.ui.model.json.JSONModel();
//Get the forecastday array table from txt_forecast object
var aData = oModel.getProperty("/forecast/txt_forecast/forecastday");
oModel1.setData({
modelData : aData
});
oTable.setModel(oModel1);
oTable.bindRows("/modelData");
oTable.placeAt('content');
And then column values will be simply provided as usual. Here image property srs is set to “icon_url” which is one of the fields of forecastday array
oTable.addColumn(new sap.ui.table.Column({
label : new sap.ui.commons.Label({
text : "Image"
}),
template : new sap.ui.commons.Image().bindProperty(
"src", "icon_url"),
width : "75px",
hAlign : "Center"
}));
One of the real fun with this API is it supports multiple languages. Below is the screenshot of the same application displayed inMarathi language.
For that, I supplied URL as var url = 'http: //api.wunderground.com/api/Your_Key/conditions/forecast/lang:MR/q/autoip.json?callback=getJSON';
To display text in different languages, we need to change the page encoding toUTF-8
For this purpose, I added below code.
<!-- Added charset='utf-8' to handle data in various langauges -->
<metahttp-equiv='X-UA-Compatible'content='IE=edge'charset='utf-8'/>
P.S – even after setting page encoding, if you execute application by web preview in eclipse, you will not see correct output. But it works perfectly fine in Google’s chrome browser.
2. SAPUI5 Application based on GeoNames wikipediaSearch JSON API
Let’s see UI of the 2nd application.
This application basically takes 2 parameters as input. Query string and max number of rows which are then passed to JSON service url as,
//GeoNames WikipediaSearch JSON API service URL
var url = 'http://api.geonames.org/wikipediaSearchJSON?formatted=true&q='
+ qString
+ '&maxRows='
+ maxRow
+ '&username=<Your_User_Name> &style=full';
3. SAPUI5 Application based on Rotten Tomatoes JSON API
Third application is based on Rotten Tomatoes JSON API which basically provides movie related data.
UI of this application is as below,
I am providing option to select different type of movies and DVDs along with number of movies to be returned. These values then mapped to URL parameter as below,
// Rotten Tomatoes API's JSON service url
var url = 'http://api.rottentomatoes.com/api/public/v1.0/lists/'
+ ddKey + '/' + ddValue
+ '.json?apikey=<your_api_key> &limit='
+ maxRow
+ '&callback=getJSON';
Here I am getting current box office movies and returning just one movie. On row selection, I am displaying the cast of the movie.
Here I am querying upcoming movies. By default, it will return 10 movies as per API documentation.
Here I am getting In Theaters movies. Based on value selected, I am setting the table title.
Please pay attention on how row selection event is handled and how 2nd table for cast of the movie got displayed. Source code can be accessed at https://github.com/CmIm/sap-ui5
4. SAPUI5 Application based on Open Weather Map JSON API
This last application is based on open weather map API and this do not required any API key or registration to web service. Below is the UI for this application,
I am getting city name and passing it to JSON URL as below,
// service url for Open Weather Map JSON API
var url = 'http://api.openweathermap.org/data/2.5/weather?q='+ qString + '&mode=json&units=metric&callback=getJSON';
Let’s see how the current weather of Walldorf, Germany looks like,
You can access the source code for all these applications at https://github.com/CmIm/sap-ui5
Closing Remarks –
This is just an attempt to share information on how we can consume JSON public APIs. I thought to develop SAPUI5 application which will access twitter, Facebook data but for that it requires access token with OAuth on which I do not have sufficient knowledge. May be some of you can try those things.
Personally the key benefit out of these things is my learning curve towards SAPUI5 significantly improved as I am experimenting various UI controls, methods on easily available JSON data. It is like getting self-assignments by looking at data and thinking different UI design, how I can utilize various UI controls etc.
The more complex JSON model, more it will be challenging to try out different UI designs and how we can make it look more better and better!
Now I am thinking why can’t we have SAPUI5 challenge on similar lines of Data Geek challenge? Here we can try out various SAPUI5 applications based on public JSON APIs. What do you guys say about this
Happy Learning and Coding!