Mobilize ALV Reports – Part 3 Visualize ALV Data with Highcharts for iPad
In my previous blog post, we created a mobile application to show ALV data. Now we will visualize data with Highcharts .
Before begin, i recommend you to read previous posts.
Part 1 – Create RESTful service
Part 2 – Create JQueryMobile Web Application with Datatables plugin
Pre-requisites:
- Check over Highcharts documentation
Steps:
- Modify index.html for charting
- Create charting rule selections dynamicly based on ALV Metadata
- Create chart data according to rule selections
- Initialize highcharts
index.html
<!DOCTYPE html>
<html>
<!–<html manifest=”app.appcache”>–>
<head>
<title></title>
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″>
<meta name=”apple-mobile-web-app-capable” content=”yes”/>
<meta name=”viewport” content=”width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no” />
<link rel=”shortcut icon” href=”images/mysapreport.jpg” />
<link rel=”stylesheet” href=”css/jqm/jquery.mobile-1.1.1.min.css” />
<!– DataTables CSS –>
<link rel=”stylesheet” type=”text/css” href=”css/datatables/jquery.dataTables.css”/>
<link rel=”stylesheet” type=”text/css” href=”css/datatables/ColVisAlt.css”/>
<script src=”js/jqm/jquery-1.7.2.min.js”></script>
<script src=”js/jqm/jquery.mobile-1.1.1.min.js”></script>
<!– DataTables –>
<script type=”text/javascript” src=”js/datatables/jquery.dataTables.min.js”></script>
<script type=”text/javascript” src=”js/datatables/ColVis.min.js”></script>
<script type=”text/javascript” src=”js/datatables/TableTools.min.js”></script>
<script type=”text/javascript” src=”js/settings.js”></script>
<script type=”text/javascript” src=”js/table.js”></script>
<script type=”text/javascript” src=”js/chart.js”></script>
<script>
$.support.cors = true;
jQuery.support.cors = true;
</script>
</head>
<body>
<div data-role=”page” id=”alv” data-theme=”e”>
<div data-role=”header” data-position=”fixed”>
<h1>MySapReport</h1>
<a href=”#settings” data-role=”button” data-icon=”gear” data-theme=”b”>Settings</a>
</div>
<div data-role=”content” >
<div data-role=”fieldcontain”>
<label for=”transaction”>Transaction:</label>
<input type=”text” name=”transaction” id=”transaction” value=”” />
</div>
<div data-role=”fieldcontain”>
<label for=”variant”>Variant:</label>
<input type=”text” name=”variant” id=”variant” value=”” />
</div>
<a href=”javascript:getALV()” data-role=”button” data-icon=”grid” data-theme=”b”>Call Report</a>
<a href=”javascript:getDemoData()” data-role=”button” data-icon=”grid” data-theme=”b”>Demo</a>
<br>
</div>
</div>
<div data-role=”dialog” id=”settings”>
<div data-role=”content” >
<label for=”connectUrl”>Connect to:</label>
<input type=”url” name=”connectUrl” id=”connectUrl” value=”” data-mini=”true” />
<label for=”username”>Username:</label>
<input type=”text” name=”username” id=”username” value=”” data-mini=”true” />
<label for=”password”>Password:</label>
<input type=”password” name=”password” id=”password” value=”” data-mini=”true” />
<br>
<a href=”javascript:exitSettings()” data-role=”button” data-icon=”delete”>Exit</a>
<a href=”javascript:saveSettings()” data-role=”button” data-icon=”check” data-theme=”b”>Save</a>
</div>
</div>
<div data-role=”page” id=”showDatatable” data-theme=”e” data-add-back-btn=”true” data-dom-cache=”true”>
<div data-role=”header” data-position=”fixed”>
<h1 id=”datatableHeader”>ALV</h1>
<div class=”ui-btn-right”>
<a href=”#chartRules” data-role=”button” data-icon=”star” data-theme=”e” data-back=”true”>Chart</a>
</div>
</div>
<div data-role=”content”>
<div id=”dynamicDatatableDIV” >
<!–<table id=”dynamicDatatable” class=”tbl_basket_style”></table>–>
</div>
</div>
</div>
<div id=”chartRules” data-role=”dialog”>
<div data-role=”header”>
<h1>Chart Rules</h1>
</div>
<section data-role=”content”>
<div data-role=”fieldcontain”>
<label for=”select-choice-ctype” class=”select”>Chart Type</label>
<select name=”select-choice-ctype” id=”select-choice-ctype” data-native-menu=”false”>
<option value=”line”>Basic Line</option>
<option value=”area”>Basic Area</option>
<option value=”bar”>Basic Bar</option>
<option value=”column”>Basic Column</option>
<option value=”pie”>Pie chart</option>
</select>
<label for=”select-choice-x” class=”select”>X-Axis</label>
<select name=”select-choice-x” id=”select-choice-x” data-native-menu=”false”></select>
<label for=”select-choice-y” class=”select”>Y-Axis</label>
<select name=”select-choice-y” id=”select-choice-y” data-native-menu=”false”></select>
<label for=”select-choice-b” class=”select”>Series</label>
<select name=”select-choice-b” id=”select-choice-b” data-native-menu=”false”></select>
<label for=”select-choice-f” class=”select”>Series Filter</label>
<select name=”select-choice-f” id=”select-choice-f” multiple=”multiple” data-native-menu=”false”></select>
</div>
<a href=”#pageChart” data-role=”button” data-icon=”grid” data-theme=”e” data-back=”true”>Show Chart</a>
</section>
</div>
<div id=”pageChart” data-role=”page” data-add-back-btn=”true” data-theme=”b”>
<header data-role=”header” data-position=”fixed”>
<h1>Chart</h1>
</header>
<section data-role=”content”>
<div id=”chartContainer” style=”width: 100%; min-width: 400px; height: 100%; margin: 0 auto”></div>
<div id=”myChart” ></div>
</section>
</div>
<script type=”text/javascript” src=”js/highcharts/highcharts.js”></script>
<script type=”text/javascript” src=”js/highcharts/modules/exporting.js”></script>
<script type=”text/javascript” src=”js/highcharts/themes/gray.js”></script>
</body>
</html>
Create charting rule selections
function fillChartingRules(){
var outputx = [];
var outputy = [];
var outputb = [];
if (!$(‘#select-choice-x’).is(‘:empty’)){
return;
}
outputx.push(‘<option>X-Axis</option>’);
outputy.push(‘<option>Y-Axis</option>’);
outputb.push(‘<option>Base</option>’);
for(i=0;i<alvMetadata.length;i++){
if (alvMetadata[i].inttype === ‘P’ || alvMetadata[i].inttype === ‘I’ ||
alvMetadata[i].inttype === ‘F’ || alvMetadata[i].inttype === ‘b’ ||
alvMetadata[i].inttype === ‘s’){
outputy.push(‘<option value=”‘ + alvMetadata[i].fieldname.toLowerCase() + ‘”>’ + alvMetadata[i].coltext + ‘</option>’);
}else{
outputx.push(‘<option value=”‘ + alvMetadata[i].fieldname.toLowerCase() + ‘”>’ + alvMetadata[i].coltext + ‘</option>’);
outputb.push(‘<option value=”‘ + alvMetadata[i].fieldname.toLowerCase() + ‘”>’ + alvMetadata[i].coltext + ‘</option>’);
}
}
$(‘#select-choice-x’).empty();
$(‘#select-choice-y’).empty();
$(‘#select-choice-b’).empty();
$(‘#select-choice-f’).empty();
$(‘#select-choice-x’).append(outputx.join(”));
$(‘#select-choice-y’).append(outputy.join(”));
$(‘#select-choice-b’).append(outputb.join(”));
try{
$(‘#select-choice-x’).selectmenu(“refresh”);
$(‘#select-choice-y’).selectmenu(“refresh”);
$(‘#select-choice-b’).selectmenu(“refresh”);
$(‘#select-choice-f’).selectmenu(“refresh”);
}catch(err){ }
}
function fillSeriesFilterData(){
var outputf = [];
var optionf;
outputf.push(‘<option>Base Filter</option>’);
for(i=0;i<alvData.length;i++){
optionf = ‘<option value=”‘ + alvData[i][$(“#select-choice-b”).val()] + ‘”>’ + alvData[i][$(“#select-choice-b”).val()] + ‘</option>’;
if (jQuery.inArray(optionf, outputf) === -1)
outputf.push(optionf);
}
outputf.sort(function(a,b) {
return (a > b) ? 1 : ((b > a) ? -1 : 0);
} );
$(‘#select-choice-f’).empty();
$(‘#select-choice-f’).append(outputf.join(”));
$(‘#select-choice-f’).selectmenu(‘refresh’, true);
}
Create chart data
function setXaxisCategories(pOptions){
alvData.sort(function(a,b) {
return (a[$(“#select-choice-x”).val()] > b[$(“#select-choice-x”).val()]) ? 1 : ((b[$(“#select-choice-x”).val()] > a[$(“#select-choice-x”).val()]) ? -1 : 0);
} );
for(i=0;i<alvData.length;i++){
if (jQuery.inArray(alvData[i][$(“#select-choice-x”).val()], pOptions.xAxis.categories) === -1)
pOptions.xAxis.categories.push(alvData[i][$(“#select-choice-x”).val()]);
}
}
function setSeries(pOptions){
var seriesData = [];
var seriesFilter = $(“#select-choice-f”).val() || [];
var hasFilter = false;
var _series = {
name:””,
data:[]
};
//initialize data array for each category
var _initData = [];
for(i=0;i<pOptions.xAxis.categories.length;i++){
_initData.push(0);
}
if (seriesFilter.length < 1){
_series.name = “”;
_series.data = _initData.slice();
seriesData.push(_series);
}else{
hasFilter = true;
for(i=0;i<seriesFilter.length;i++){
var _seriesF = {
name:””,
data:[]
};
_seriesF.name = seriesFilter[i];
_seriesF.data = _initData.slice();
seriesData.push(_seriesF);
}
}
var xValue;
var yValue;
var _index;
var _seriesindex;
var _filter;
for(i=0;i<alvData.length;i++){
xValue = alvData[i][$(“#select-choice-x”).val()];
yValue = alvData[i][$(“#select-choice-y”).val()];
_index = jQuery.inArray(xValue, pOptions.xAxis.categories);
_seriesindex = 0;
if (hasFilter) {
_filter = alvData[i][$(“#select-choice-b”).val()];
if (jQuery.inArray(_filter, seriesFilter) !== -1){
for(j=0;j<seriesData.length;j++){
if (_filter === seriesData[j].name){
_seriesindex = j;
break;
}
}
}else{
continue;
}
}
if (isNaN(seriesData[_seriesindex].data[_index]))
seriesData[_seriesindex].data[_index] = parseFloat(yValue);
else
seriesData[_seriesindex].data[_index] = seriesData[_seriesindex].data[_index] + parseFloat(yValue);
//set 2 decimal point
seriesData[_seriesindex].data[_index] = parseFloat(seriesData[_seriesindex].data[_index].toFixed(2));
}
pOptions.series = seriesData;
}
Initialize Highcharts
function basicChart(){
var options = {
chart: {
renderTo: ‘chartContainer’,
defaultSeriesType: $(“#select-choice-ctype”).val()//pie, column, bar, line
},
title: {
text: reportDescription
},
subtitle: {
text: $(‘#select-choice-x option:selected’).text() + ‘ / ‘ + $(‘#select-choice-y option:selected’).text()
},
legend: {
layout: ‘vertical’,
align: ‘right’,
verticalAlign: ‘top’,
x: -10,
y: 100,
borderWidth: 0
},
xAxis: {
categories: [],
labels: {
rotation: -45,
align: ‘right’,
style: {
fontSize: ’13px’,
fontFamily: ‘Verdana, sans-serif’
}
},
title: {
text: $(‘#select-choice-x option:selected’).text()
}
},
yAxis: {
title: {
text: $(‘#select-choice-y option:selected’).text()
}
},
series: []
};
setXaxisCategories(options);
setSeries(options);
// Create chart
var chart = new Highcharts.Chart(options);
}
Test
You may use MySapReport for testing.
Rule definions of chart;
- Chart Type (Mandatory): There are 5 kind of charts predefined (Line, Area, Bar, Column, Pie).
- X-Axis (Mandatory): Used as category axis. Only non-numerical alv columns can be chosen.
- Y-Axis (Mandatory): Used as value type axis. Only numeric alv columns can be chosen.
- Series (Optional): Used as collection of datapoints in category axis.
- Series Filter (Optional): Used as filter for series, filled by selection event of Series and values are derived dynamicly from alv data.
You can find all sources codes on https://github.com/basarozgur/MySapReport
Screen Shots:
Chart rule definition
Basic Line
Basic Area
Basic Bar
Basic Column
Pie
Thank you very much Basar for posting such advanced concept in reporting. I find it very helpful as I am exploring on this topic these days.
Regards,
Rashmith.