Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
GrahamRobbo
Active Contributor

This blog post is at the request of abesh.bhattacharjee - blame him :wink:


The SAPUI5 library includes chart controls based upon the VIZ charting library. You can see some samples of them in action at https://sapui5.hana.ondemand.com/sdk/test-resources/sap/viz/Charting.html

Once you start building charts you pretty soon want to implement some event handling when a user clicks on a chart element. For example the usual way of implementing drill-down is by having the user click on a chart element and then using that element as the context for subsequent processing - perhaps rendering another chart that shows a more granular representation of just the element selected.

The SAPUI5 eventing framework allows us to attach an handler to the selectData event to be called when a user clicks on a chart element. The framework passes a sap.ui.base.Event object to the handler that contains the details of the specific action and target that triggered the event. In a perfect world we would be able to extract from this object the context of the element we clicked on so we could use that for subsequent actions.

In simple charts that only have a single dimension and a single measure this is achievable - albeit not terribly easily. There is an example of how to do it Re: Need dimensions value on in VIZ bar Chart dataselect event.

But when you are charting multiple measurements and/or multiple dimensions things become much more difficult. Take for example this stacked column chart.

If you want to follow along my example yourself here is the HTML/Javascript that created it.

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Chart</title>
<script id="sap-ui-bootstrap" type="text/javascript"
  src="resources/sap-ui-core.js"
  data-sap-ui-libs="sap.ui.commons, sap.viz">
</script>
<script>
  var oModel = new sap.ui.model.json.JSONModel({
    data : [
      { country : 'China', product : 'Car', profit : 32 },
      { country : 'France', product : 'Car', profit : 43 },
      { country : 'Germany', product : 'Car', profit : 34 },
      { country : 'USA', product : 'Car', profit : 25 },
      { country : 'China', product : 'Truck', profit : 78 },
      { country : 'France', product : 'Truck', profit : 86 },
      { country : 'Germany', product : 'Truck', profit : 56 },
      { country : 'USA', product : 'Truck', profit : 76 },
      { country : 'China', product : 'Motorcycle', profit : 78 },
      { country : 'France', product : 'Motorcycle', profit : 86 },
      { country : 'Germany', product : 'Motorcycle', profit : 56 },
      { country : 'USA', product : 'Motorcycle', profit : 76 },
      { country : 'China', product : 'Bicycle', profit : 78 },
      { country : 'France', product : 'Bicycle', profit : 86 },
      { country : 'Germany', product : 'Bicycle', profit : 56 },
      { country : 'USA', product : 'Bicycle', profit : 76 }
    ]
  });
  sap.ui.getCore().setModel(oModel);
  var dataset = new sap.viz.ui5.data.FlattenedDataset({
    dimensions : [
      { axis : 1, name : 'Country', value : "{country}" },
      { axis : 2, name : 'Product', value : "{product}" }
    ],
    measures : [
      { name : 'Profit', value : '{profit}' }
    ],
    data : { path : "/data" }
  });
  new sap.viz.ui5.StackedColumn("oChart",{
    width : "100%",
    height : "200px",
    title : { visible : true, text : 'Test Stacked Column Chart' },
    dataset : dataset
  }).placeAt("content");
</script>
</head>
<body class="sapUiBody">
  <div id="content"></div>
</body>
</html>

Notice that we have two dimensions - Country and Product. We can select an individual element by clicking on it, we can select an entire column by clicking on the X-axis label, and we can select many elements together.

Each time we click a chart element we fire the selectData event which passes details of the element we clicked on to any event handlers. The challenge is to determine the context of this element for subsequent processing. What follows is one way you could do this by adding a few lines of javascript to the HTML page.

Step 1. Turn off all the user interaction that the chart library delivered with the exception of tooltips.

  sap.ui.getCore().byId("oChart").setInteraction(
    new sap.viz.ui5.types.controller.Interaction({
      selectability: new sap.viz.ui5.types.controller.Interaction_selectability({
        mode: sap.viz.ui5.types.controller.Interaction_selectability_mode.none
      })
    })
  );

Step 2. Attach an event handler for the browser click event to the chart.

  sap.ui.getCore().byId("oChart").attachBrowserEvent("click",chartClickHandler);

Step 3: Implement the event handler.

We are only interested in clicks on the x-Axis of the chart. The x-Axis markup is wrapped in a SVG tag that looks like <g class="v-m-xAxis" ...>. So firstly we test to see if the area clicked is not a descendant of his tag and return immediately.

if(!$(oEvent.srcElement).closest('.v-m-xAxis').length) return;

The x-Axis labels are rendered like the below sample. The <rect> tag is the source element of the click event.

<rect x="301" y="1" width="301" height="35" opacity="0" class="v-labelarea" fill="#808080"></rect>

<g fill="#333333" class="v-label viz-axis-label" font-size="12px" font-weight="normal" font-family="'Open Sans', Arial, Helvetica, sans-serif">

  <text pointer-events="none" x="452" y="18.2" dominant-baseline="middle" text-anchor="middle">France</text>

</g>

We can isolate the contents of the <text> tag into a variable called xAxisLabel like this.

    var xAxisLabel=$($(oEvent.srcElement).next('.viz-axis-label').children('text')).text();

If I add a dialog box to confirm the x-Axis label that has been clicked the complete event handler looks like this.

  function chartClickHandler(oEvent) {
    if(!$(oEvent.srcElement).closest('.v-m-xAxis').length) return;
    var xAxisLabel=$($(oEvent.srcElement).next('.viz-axis-label').children('text')).text();
    new sap.ui.commons.Dialog({ title: xAxisLabel+" clicked" }).open();
  }

It's not a perfect solution but it works for our example. Obviously the more complex the chart the more challenging isolating the context of a user click event will be.

If, like me, you are amazed at what can be done with a few lines of Javascript I encourage you to check out JQuery.

And just for completeness here is the final version.

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Chart</title>
<script id="sap-ui-bootstrap" type="text/javascript"
  src="resources/sap-ui-core.js"
  data-sap-ui-theme="sap_goldreflection"
  data-sap-ui-libs="sap.ui.commons, sap.viz">
</script>
<script>
  var oModel = new sap.ui.model.json.JSONModel({
    data : [
      { country : 'China', product : 'Car', profit : 32 },
      { country : 'France', product : 'Car', profit : 43 },
      { country : 'Germany', product : 'Car', profit : 34 },
      { country : 'USA', product : 'Car', profit : 25 },
      { country : 'China', product : 'Truck', profit : 78 },
      { country : 'France', product : 'Truck', profit : 86 },
      { country : 'Germany', product : 'Truck', profit : 56 },
      { country : 'USA', product : 'Truck', profit : 76 },
      { country : 'China', product : 'Motorcycle', profit : 78 },
      { country : 'France', product : 'Motorcycle', profit : 86 },
      { country : 'Germany', product : 'Motorcycle', profit : 56 },
      { country : 'USA', product : 'Motorcycle', profit : 76 },
      { country : 'China', product : 'Bicycle', profit : 78 },
      { country : 'France', product : 'Bicycle', profit : 86 },
      { country : 'Germany', product : 'Bicycle', profit : 56 },
      { country : 'USA', product : 'Bicycle', profit : 76 }
    ]
  });
  sap.ui.getCore().setModel(oModel);
  var dataset = new sap.viz.ui5.data.FlattenedDataset({
    dimensions : [
      { axis : 1, name : 'Country', value : "{country}" },
      { axis : 2, name : 'Product', value : "{product}" }
    ],
    measures : [
      { name : 'Profit', value : '{profit}' }
    ],
    data : { path : "/data" }
  });
  new sap.viz.ui5.StackedColumn("oChart",{
    width : "100%",
    height : "200px",
    title : { visible : true, text : 'Test Stacked Column Chart' },
    dataset : dataset
  }).placeAt("content");
//Step 1. Turn off all the user interaction stuff in the chart apart from tooltips.
  sap.ui.getCore().byId("oChart").setInteraction(
    new sap.viz.ui5.types.controller.Interaction({
      selectability: new sap.viz.ui5.types.controller.Interaction_selectability({
        mode: sap.viz.ui5.types.controller.Interaction_selectability_mode.none
      })
    })
  );
//Step 2. Attach an event handler for when the user clicks on the chart.
  sap.ui.getCore().byId("oChart").attachBrowserEvent("click",chartClickHandler);
//Step 3. Implementation of the event handler.
  function chartClickHandler(oEvent) {
    if(!$(oEvent.srcElement).closest('.v-m-xAxis').length) return;
    var xAxisLabel=$($(oEvent.srcElement).next('.viz-axis-label').children('text')).text();
    new sap.ui.commons.Dialog({ title: xAxisLabel+" clicked" }).open();
  }
</script>
</head>
<body class="sapUiBody">
  <div id="content"></div>
</body>
</html>

Cheers

Graham Robbo

61 Comments