Highcharts Drilldown Functionality With Angular Custom Directive And Web API

This article will demonstrate how to create "Add drilldown" (basically it is called nested charts) functionality using Highcharts Library in Angular JS with Web API.

This is the second part of this series, “Highcharts with Angular JS custom directive and Web API”. In the first part, we went through the preparation of the charts using the Highcharts Library and Web API. The first part covered database table creation with dummy data, the creation of a connection with a database using ADO.NET Entity Data Model, fetching data from a database using API Controller, and to show these data in form of charts. We have created Custom Directive in Angular JS which plotted our data in different types of charts based on the chart type.

This demonstration will cover all the steps which are required to show drilldown charts on click of the first chart plotted point.

The whole demonstration is divided into three different parts, as follows.

  • Part 1: Create charts using Highcharts Library and AngularJS custom directive with Web API
  • Part 2: Implement Drilldown functionality with charts [Nested charts]
  • Part 3: Show loading image/message when rendering the charts

So, let’s move to practical implementation of PART 2 and create drill-down charts. This demonstration will cover only the drilldown chart and we will use existing codes which we have created in PART 1. So, to understand it clearly just follow up PART 1.

Below are the objectives of this article which will be covered step by step as following.

  • TASK 1: Get data for drilldown chart from API Controller
  • TASK 2: Add services method to get data from API
  • TASK 3: Add drilldown event for the chart

So, let’s start with task one.

TASK 1 - Get data for drilldown chart from API Controller

As we have already created API Controller as “CricketController” in part 1 now we will add one more Action method which will get the data for drilldown chart. So, basically in the first part, we are getting the data for “Top ODI Batsmen in India”. If we click or select any batsman to see how he/she scored run year wise, then it will plot a new chart in the same section and this is called Drilldown chart or Nested charts. So, add following new action method as following.

  1. [Route("Cricket/RunScoredByYear")]  
  2. [HttpGet]  
  3. public IHttpActionResult RunScoredByYear()  
  4. {  
  5.     try  
  6.     {  
  7.         using (var db = new BatsmenEntities())  
  8.         {  
  9.             var result = db.BatsmenRuns.ToList(); ;  
  10.             return Ok(result);  
  11.         }  
  12.     }  
  13.     catch (System.Exception)  
  14.     {  
  15.         throw;  
  16.     }  
  17. }  

Above method “RunScoredByYear” will give you the list of Scored Runs by a single player yearly.

TASK 2 - Add services method to get data from API

To fetch data from API to client side we are using services in Angular JS. In angular js, service is the best to get data.  Actually, service is already created in part one, so what we need to do is to just add one method to get data for scored runs by year as highlighted.

  1. (function (app) {  
  2.     'use strict'  
  3.   
  4.     app.factory('ChartService', ['$http', ChartService]);  
  5.   
  6.     function ChartService($http) {  
  7.   
  8.         var getTopBatsmenInODI = function () {  
  9.           return $http.get('/Cricket/TopBatsmenInODI');  
  10.         };  
  11.   
  12.         var getRunScoredByYear = function () {  
  13.             return $http.get('/Cricket/RunScoredByYear');  
  14.         };  
  15.   
  16.         return {  
  17.             topBatsmenInODI: getTopBatsmenInODI,  
  18.             runScoredByYear: getRunScoredByYear  
  19.         }  
  20.     };  
  21. })(angular.module("chartApp"));  

TASK 3 - Add drilldown event to the chart

A drilldown chart is nested functionality of the Highcharts so there are two events which are responsible to create a drilldown chart and these are “drilldown” and “drillup”. Drilldown event is basically used to create a nested chart and if you would like to come back again, then we have to use the drillup event.

If we select or click on any point which indicates specific player records, it will show you a drilldown chart. But when we add drilldown chart, we have to add one extra attribute to the data fo the main chart, that is called “drilldown” as following.

  1. _.forEach(scope.data, function (item) {  
  2.   scope.categories.push(item.Name);  
  3.   
  4. //Adding drilldown name here  
  5.   scope.seriesData.push({ name: item.Name, y: item.TotalRuns, drilldown: item.Name });  
  6. });  
  7.   
  8. $timeout(function () {  
  9.   scope.chartConfig.addSeries({  
  10.     name: 'Top ODI Batsman',  
  11.     data: scope.seriesData  
  12.   });  
  13. }, 2000);  

So, once we will click on any plotted point, it will fetch the name of the drilldown and create the drilldown based on name.

To create drilldown chart, we need data that we have already got from service in controller of the custom directive as below. So, here, the data is available in “runScoredByYear” score variable that will be used further in drilldown event.

  1. controller: function ($scope, ChartService) {  
  2.   $scope.runScoredByYear = [];  
  3.   getRunScoredByYear();  
  4.   
  5.   function getRunScoredByYear() {  
  6.     ChartService.runScoredByYear().then(function (response) {  
  7.       $scope.runScoredByYear = response.data;  
  8.     });  
  9.   }  
  10. }  

So, we can see below code, there are two events for the chart and both are responsible to implement drilldown and drillup functionality. Once the data will be available in the drilldown event, we can get the chart instance and add new series to this as following.

  1. chart: {  
  2.     type: scope.type,  
  3.     renderTo: 'chart_container_' + scope.type,  
  4.     events: {  
  5.     drilldown: function (e) {  
  6.       if (!e.seriesOptions) {  
  7.         var chart = this;  
  8.   
  9.         var selectedColumn = e.point.name;  
  10.         var scoredRunFilteredData = _.filter(scope.runScoredByYear, { 'Name': selectedColumn });  
  11.   
  12.         var scoredRunFilteredDataPoints = [];  
  13.   
  14.         for (var i = 0; i < scoredRunFilteredData.length; i++) {  
  15.           scoredRunFilteredDataPoints.push({ name: scoredRunFilteredData[i].Year, y: scoredRunFilteredData[i].Runs });  
  16.         }  
  17.   
  18.         chart.drilldowns = {  
  19.           series1: {  
  20.             type: 'line',  
  21.             name: selectedColumn,  
  22.             data: scoredRunFilteredDataPoints,  
  23.             color: 'red'  
  24.           }  
  25.         };  
  26.   
  27.         var series = [chart.drilldowns.series1];  
  28.   
  29.         setTimeout(function () {  
  30.           chart.addSingleSeriesAsDrilldown(e.point, series[0]);  
  31.           chart.setTitle({ text: e.point.name + ' <b>Scored Runs</b>' });  
  32.           chart.applyDrilldown();  
  33.         }, 1000);  
  34.       }  
  35.     },  
  36.     drillup: function (e) {  
  37.       var chart = this;  
  38.       chart.setTitle({ text: 'Top ODI Batsman in India' });  
  39.     }  
  40.     }  
  41. }  

When drilldown chart will be plotted then there will be a Back button on the top. That is used to come back on the main chart. So, basically, here we are drilling up. On drillup event, we can customize the main chart again like change the title of the chart etc.

So, it is time to run the application and click on any plotted point. Below is the first or we can say the main chart where “Top ODI Batsmen in India” data is plotted;

Angular

If you click on any of the plotted points for any batsman, the drilldown chart will be plotted with their scored run by year as follows. For this demonstration, I am choosing “Sachin Tendulkar” and click to point where data for Sachin Tendulkar is plotted on every chart.

Note
We can change the chart type in drilldown as well.

Angular

Here is whole code for “ChartDirective.JS”.

  1. (function (app) {  
  2.     'use strict'  
  3.   
  4.     app.directive('chartDirective', ['$timeout', chartDirective]);  
  5.   
  6.     function chartDirective($timeout) {  
  7.         return {  
  8.             restrict: 'E',  
  9.             templateUrl: function (element, attrs) {  
  10.                 return "/App/directive/" + attrs.type + 'ChartTemplate.html'  
  11.             },  
  12.             scope: {  
  13.                 type: '@',  
  14.                 data: '='  
  15.             },  
  16.             link: function (scope, element, attribute) {  
  17.                 scope.chartConfig = new Highcharts.Chart({  
  18.                     chart: {  
  19.                         type: scope.type,  
  20.                         renderTo: 'chart_container_' + scope.type,  
  21.                         events: {  
  22.                             drilldown: function (e) {  
  23.                                 if (!e.seriesOptions) {  
  24.                                     var chart = this;  
  25.   
  26.                                     var selectedColumn = e.point.name;  
  27.                                     var scoredRunFilteredData = _.filter(scope.runScoredByYear, { 'Name': selectedColumn });  
  28.   
  29.                                     var scoredRunFilteredDataPoints = [];  
  30.   
  31.                                     for (var i = 0; i < scoredRunFilteredData.length; i++) {  
  32.                                         scoredRunFilteredDataPoints.push({ name: scoredRunFilteredData[i].Year, y: scoredRunFilteredData[i].Runs });  
  33.                                     }  
  34.   
  35.                                     chart.drilldowns = {  
  36.                                         series1: {  
  37.                                             type: 'line',  
  38.                                             name: selectedColumn,  
  39.                                             data: scoredRunFilteredDataPoints,  
  40.                                             color: 'red'  
  41.                                         }  
  42.                                     };  
  43.   
  44.                                     var series = [chart.drilldowns.series1];  
  45.   
  46.                                     setTimeout(function () {  
  47.                                         chart.addSingleSeriesAsDrilldown(e.point, series[0]);  
  48.                                         chart.setTitle({ text: e.point.name + ' <b>Scored Runs</b>' });  
  49.                                         chart.applyDrilldown();  
  50.                                          
  51.                                     }, 1000);  
  52.                                 }  
  53.                             },  
  54.                             drillup: function (e) {  
  55.                                 var chart = this;  
  56.                                 chart.setTitle({ text: 'Top ODI Batsman in India' });  
  57.                                 chart.();  
  58.                             }  
  59.                         }  
  60.                     },  
  61.                     title: {  
  62.                         text: 'Top ODI Batsman in India'  
  63.                     },  
  64.   
  65.                     xAxis: {  
  66.                         type: 'category',  
  67.                         categories: []  
  68.                     },  
  69.                     yAxis: {  
  70.                         title: {  
  71.                             text: 'Runs'  
  72.                         },  
  73.                         plotLines: [{  
  74.                             value: 0,  
  75.                             width: 1,  
  76.                             color: '#808080'  
  77.                         }]  
  78.                     },  
  79.                     tooltip: {  
  80.                         valueSuffix: ' Run Scored'  
  81.                     }  
  82.                 });  
  83.   
  84.                 scope.seriesData = [];  
  85.                 scope.categories = [];  
  86.   
  87.                 scope.$watch('data'function (newValue) {  
  88.                     if (newValue != undefined) {  
  89.   
  90.                         _.forEach(scope.data, function (item) {  
  91.                             scope.categories.push(item.Name);  
  92.                             scope.seriesData.push({ name: item.Name, y: item.TotalRuns, drilldown: item.Name });  
  93.                         });  
  94.   
  95.                         $timeout(function () {  
  96.                             scope.chartConfig.addSeries({  
  97.                                 name: 'Top ODI Batsman',  
  98.                                 data: scope.seriesData  
  99.                             });  
  100.                         }, 2000);  
  101.                     }  
  102.                 });  
  103.   
  104.             },  
  105.             controller: function ($scope, ChartService) {  
  106.                 $scope.runScoredByYear = [];  
  107.                 getRunScoredByYear();  
  108.   
  109.                 function getRunScoredByYear() {  
  110.                     ChartService.runScoredByYear().then(function (response) {  
  111.                         $scope.runScoredByYear = response.data;  
  112.                     });  
  113.                 }  
  114.             }  
  115.         }  
  116.     }  
  117. })(angular.module("chartApp"));  

Now, everything has been set up, and it’s time to run your application. Run and enjoy.

Conclusion

Today, we have learned how to implement Drilldown chart functionality using Highcharts and AngularJS custom directive and Web API.

I hope this post helps you. Please put your feedback which will help me improve the next post. If you have any doubts, please ask in the comments section.


Similar Articles