Search code examples
javascriptangularjsarrayshighchartshighmaps

Array sets to null when bound to highcharts/highmaps directive


I'm using highmaps to create a vector map that can change based on the data values loaded into the series[0].data array. The initial chart is created with my default values, and inserted into the page using a custom directive. When a select dropdown changes value, I fire off a function using ng-change to update the chart values.

My ng-change function fires and runs as expected, until I attempt to access the series containing the data used to populate highmaps. It seems that after the initial chart is created, the series array is given a value of null. This persists even when I attempt to load in a fresh chartOptions object. I've noticed that when the $scope object containing the chartOptions isn't bound to the directive, then the series is populated with the correct values. The array is only empty when it is bound to the directive.

This only happens with my highmaps. All of the other highcharts on the page have their series arrays visible for editing after being two-way bound to the directive. Also all other elements of my highmap chartOptions object can be viewed and edited, just not the part with the actual data.

My directive

.directive('mapChart', function(){
return {
        restrict: "E",
        replace: true,
        template: "<div id='map' class='div_content-holder'></div>",
        scope:{
            options:"=",
        },
        link: function(scope, element){
                Highcharts.mapChart(element[0], scope.options);
            });
        }
    };
});

My html

<map-chart options="region_options"></map-chart>

my JavaScript

$scope.region_options = {};

var setRegionMap = function(jsonData){
        var chartOptions = selectChart('map'); // grabs my premade options object 

        // grabs data from somewhere else and adds it to chart options
        chartOptions.series[0].data.forEach(function (region, index) {
           region["value"] = $scope.region_map.one_month[index];
           console.log(chartOptions.series[0].data); //says that series exists
        });
        $scope.region_options = chartOptions;
        console.log($scope.region_options); //says that .series is null, everything else is still there
        //$scope.$apply(); //i do this elsewhere  in my ajax call
    };

Is this an issue with two-way binding the array with highmaps, or angular, or both?


Solution

  • You can change your directive in the following way, adding a new parameter for the current data. Adding the two watches you will ensure that every time you change data from outside, the chart will be refreshed setting up the correct data and with the first watch on the option you will ensure to instantiate the chart when your options have really been loaded:

    .directive('mapChart', function(){
      return {
        restrict: "E",
        replace: true,        
        template: "<div id='map' class='div_content-holder'></div>",   
        scope:{
          options:"=",
          current: "=" // your actual data        
        },        
        link: function(scope, element){
          var chartexample = {};       
          scope.$watch('options', function() {
            chartexample = Highcharts.mapChart(element[0], scope.options);
          });
          scope.$watch('current', function(){
            chartexample.series[0].setData(scope.current);
          });            
        }
      }
    });
    

    You can get rid of both the watchers. About the init, you just have to be sure to init your directive once you have already all your options, so you don't need anymore the watch on the options. About the current data, you can add a callback parameter & in the directive which will update the values of the chart, and call this callback of the directive inside the function associated to the ngChange of the select inside the controller.

    This will make the code better and cleaner. But for now you can enjoy your chart in this way :)

    My best luck for your app!

    Cheers :)