0

I'm building a webpage that uses Highcharts to visualize some data. To make it reusable, I've wrapped the chart that I want in a directive

'use strict';
angular.module('statisticsApp')
  .directive('cleanbarchart', function () {
    scope:{
      localdata:'@'
    }
    return {
      template: '<div></div>',
      restrict: 'E',
      link: function postLink(scope, element, iAttrs) {
          // console.log(iAttrs);
          // console.log(iAttrs);
          // var attrs = JSON.parse(iAttrs.id);
          var attrs = iAttrs;
          element.highcharts({
            chart: {
              type: 'column',
              width:1000,
              zoomType: 'x'
            },
            title: {
              text: scope.localdata[attrs.id].title //title
            },
            xAxis: {
              categories: scope.localdata[attrs.id].categories, crosshair: true

            },
            yAxis: {
              min: 0
            },
            tooltip: {
  //            headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
  //            pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
  //            '<td style="padding:0"><b>{point.y:.1f} mm</b></td></tr>',
  //            footerFormat: '</table>',
  //            shared: true,
  //            useHTML: true
            },
            plotOptions: {
  //            column: {
  //            pointPadding: 0.2,
  //            borderWidth: 0
  //          }
            },

            series: scope.localdata[attrs.id].series
          })
      }
    };
  });

In my controller, I use a service and a callback function to populate the localdata

angular.module('statisticsApp')
  .controller('AboutCtrl', function ($scope, HttpDataService) {

     function done(data){

       console.log(data);
       $scope.localdata['test2'] = data; //HttpDataService.getUniqueUsers() ;
     }

     $scope.localdata = {} ;
     HttpDataService.getUniqueUsers(done) ;
});

with a service that looks like this:

angular.module('statisticsApp')
  .service('HttpDataService', function($http, $q, baseRestPath) {
    // AngularJS will instantiate a singleton by calling "new" on this function
    return {
      getUniqueUsers: function (callback, periodicity) {
        var url = baseRestPath + '/sessions/uniqueUsers';
        console.log(url);
        var dates = [];
        var values = [];

        $http.get(url).then(
          function successCallback(response){
            var data = response.data;
            data.forEach(function(dataLine) {
              dates.push(dataLine[1]);
              values.push(dataLine[0]);
            })
            console.log(values);
            callback({title: 'Unique Users', categories:dates, 'series': [ {name: 'Alltime', data:values} ]  });
        //    return {'title': "Unique Users", 'categories':dates, 'series': [ {name: "Alltime", data:values} ]  }
          },function errorCallBack(response){
            //do nothing
          }
        );


//      return {'title': "Unique Users", 'categories':dates, 'series': [ {name: "Alltime", data:values} ]  }
      }
    }
  });

and finally, in my html i use the following code to call the directive

<cleanbarchart id="test2"></cleanbarchart>

while I'm certain that my service works, and returns the data correctly, I get the error

Cannot read property 'title' of undefined

I think it has to do with the asynchronous way that $http works, but I can't figure out how to make the directive watch on either scope.localdata or scope.localdata[attrs.id] which I have tried by wrapping the element.highcharts in the watch block

link: function postLink(scope, element, iAttrs) {
  scope.$watch('localdata',function(){
    element.highcharts.....
  }
}

or 

link: function postLink(scope, element, iAttrs) {
  scope.$watch('localdata[' + attrs.id + ']',function(){
    element.highcharts.....
  }
}

All help appreciated

1 Answer 1

2

Firsty you need to change your service to return a promise instead of passing a callback to your service. If you open $http documentation you will see that $http.get returns HttpPromise object which you can later resolve.

After you done that you can pass promise to directive instead of data.

So instead of:

$scope.localdata = {} ;
HttpDataService.getUniqueUsers(done) ;

Change to:

$scope.localdataPromise = HttpDataService.getUniqueUsers();

And later in directive resolve it like that:

scope.localdataPromise.then(function (data) { /* resolve data and draw chart */ });
Sign up to request clarification or add additional context in comments.

4 Comments

thank you very much. Additional question, is there a way to put the watch in the controller instead of the directive, in case I want to reformat the data ?
You can chain your promise resolvers like that: var promise = $http.get(...); promise.then(...); promise.then(...);and so on
I can't get it to work if I want to use a watcher for some radio buttons to change te data in my chart. The watch works, but the data doesn't refresh
If you still want to use watches then try setting objectEquality parameter to true. You can read more in documentation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.