Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Highcharts dynamic (re-)sizing in AngularJS tabs

My application displays HighCharts charts in various tabs (using AngularJS). Note that the charts are re-generated on the fly every time a tab is selected (Meaning that Angular "removes or recreates a portion of the DOM tree" every time).

My problem is that the size of the chart is correct only the first time I click on a tab. When I switch tabs, the charts that are created largely exceed the size of their container. Surprisingly, the charts are correctly resized after a window resize (i.e. when chart.reflow() is called).

So I tried the following, which did not help:

$(element).highcharts({
    chart: {
      type: 'scatter',
      zoomType: 'xy',
      events: {
        load: function () {
          this.reflow();
        }
      }
    },
    ...

Finally, the following did work, but it feels like a hack.

$(element).highcharts({
    chart: {
      type: 'scatter',
      zoomType: 'xy',
      events: {
        load: function () {
          var chart = this;
          setTimeout(function () { chart.reflow(); }, 0);
        }
      }
    },
    ...

Any idea where the problem comes from? I am spending hours on this, and this is pretty frustrating...

EDIT: I have another related question: By default my charts have a given size, but I have an "enlarge" button to make them take the full space. The button simply toggles a CSS class to do that. However, doing this does not trigger a resize event, and so does not trigger a reflow. So I have the same problem of charts not filling their container.

Here is a fiddle to demonstrate the issue: http://jsfiddle.net/swaek268/3/

Again, I have found a way around the problem, but it feels like an even bigger hack.

var width = 0;
var planreflow = function(){
  setTimeout(function(){
      if(width!==$('#container').width()){
          $('#container').highcharts().reflow(); 
          console.log('reflow!')
      }
      width = $('#container').width();
      planreflow();
  }, 10);
};
planreflow();
like image 308
Eric Leibenguth Avatar asked May 23 '15 15:05

Eric Leibenguth


Video Answer


1 Answers

I think I found solutions to both of my problems:

  1. Wrong size when chart finishes loading:

The problem does not come from Highcharts, but from the timing when the chart is created. I directly called .highcharts() from the link() function of my Angular directive. The JS looking essentially like this:

app.directive('dynamicView', function() {
  return {
    link: function(scope, element, attrs){
      $(element).highcharts({...});
    },    
  };
});

As my element also has a ng-if directive depending on whether the tab is selected or not, I imagine that when that condition turns true, angular calls the link() function before the dimension of the element is determined (I guess the HTML has to be ready in order to determine the size of the elements). So I think the problem was that I was calling .highcharts() before the document was ready. For some reason that I still don't understand, the size of the element is correct the first time ng-if turns true, but incorrect the next times.

Anyway, a solution (still not very elegant - but better than calling reflow()) is to delay the call to .highcharts() itself, to make sure that the HTML is ready when creating the charts:

link: function(scope, element, attrs){
  setTimeout(function () {
    $(element).highcharts({...});
  }, 0);
}

Thank you @Pavel Fus for your comments on the behaviour of .highcharts() and setTimeout().

  1. Chart not resizing when toggling a class

For this problem I took advantage of Angular's eventing system.

In my controller controlling the full-screen button:

$scope.toggleFullScreen = function(e){
  $scope.$broadcast('fullscreen');
}

This event is passed down the hierarchy, ending-up to my directives containing the charts. All I have to do, when creating my charts is to listen to this event, and trigger a reflow() accordingly:

scope.$on('fullscreen', function(){
  setTimeout(function () { 
    $(element).highcharts().reflow(); 
  }, 0);
});

Note that --again-- the trick only works if I delay the reflow with setTimeout(). Not sure why.

I hope this helps! Spent quite a bit of time on this...

like image 181
Eric Leibenguth Avatar answered Oct 17 '22 01:10

Eric Leibenguth