3
votes

I'm using Kendo UI to add charts to an AngularJS application.

The charts work fine, except when they start hidden: if a chart's <div /> is hidden when the chart is initialized, when the chart's <div /> is shown the chart does not span the whole available width and is rendered in only a fraction of the available space.

You can test this issue with the following code:

<div data-ng-app='myModule' data-ng-controller='myController' data-ng-cloak>
  <p>
    <button data-ng-click='toggleMyChart()'>
      Toggle chart
    </button>
  </p>
  <div data-kendo-chart='myChart' data-k-options='myChartOptions' data-ng-show='isMyChartVisible'>
  </div>
</div>
<script>
  (function () {
    angular
      .module('myModule', ['kendo.directives'])
      .controller('myController', ['$scope', function ($scope) {
        $scope.isMyChartVisible = false;

        $scope.toggleMyChart = function () {
          $scope.isMyChartVisible = !$scope.isMyChartVisible;
        };

        $scope.myChartOptions = {
          'dataSource': {
            'data': [
              { 'category': '1', 'value': '4' },
              { 'category': '2', 'value': '8' },
              { 'category': '3', 'value': '15' },
              { 'category': '4', 'value': '16' },
              { 'category': '5', 'value': '23' },
              { 'category': '6', 'value': '42' }
            ]
          },
          'series': [
            { 'field': 'value', 'name': 'Numbers' }
          ],
          'seriesDefaults': {
            'categoryField': 'category',
            'type': 'bar'
          }
        };
      }]);
  })();
</script>

Demo: http://codepen.io/albireo/pen/AeHui/?editors=101

The problem seems to be not strictly related to AngularJS, as it happens when using jQuery too:

<p>
  <button id='toggleChart'>
    Toggle chart
  </button>
</p>
<div id='chart'>
</div>
<script>
  (function () {
    'use strict';
    $(document).ready(function () {
      var isChartVisible = false;

      $('#toggleChart').click(function () {
        isChartVisible = !isChartVisible;

        if (isChartVisible) {
          $('#chart').show();
        } else {
          $('#chart').hide();
        }
      });

      $('#chart').hide();

      $('#chart').kendoChart({
        'dataSource': {
          'data': [
            { 'category': '1', 'value': '4' },
            { 'category': '2', 'value': '8' },
            { 'category': '3', 'value': '15' },
            { 'category': '4', 'value': '16' },
            { 'category': '5', 'value': '23' },
            { 'category': '6', 'value': '42' }
          ]
        },
        'series': [
          { 'field': 'value', 'name': 'Numbers' }
        ],
        'seriesDefaults': {
          'categoryField': 'category',
          'type': 'bar'
        }
      });
    });
  })();
</script>

Demo: http://codepen.io/albireo/pen/mvBap?editors=101

The problem seems to be known to Telerik:

when you click the button and show the chart it is already rendered and takes the default chart's width

Quote taken from Kendo UI Chart does not use the entire width of the div when the div containing the chart is hidden.

Indeed, if I add a .redraw() to the jQuery example everything works correctly:

$('#toggleChart').click(function () {
  isChartVisible = !isChartVisible;

  if (isChartVisible) {
    $('#chart').show();
  } else {
    $('#chart').hide();
    $('#chart').data('kendoChart').redraw();
  }
});

Demo: http://codepen.io/albireo/pen/atkLg?editors=101

Then I tried to add the same call to the AngularJS test too but it didn't work, nothing changed:

$scope.toggleMyChart = function () {
  $scope.isMyChartVisible = !$scope.isMyChartVisible;

  if ($scope.isMyChartVisible) {
    $scope.myChart.redraw();
  }
};

Demo: http://codepen.io/albireo/pen/IFcbw?editors=101

I think this is caused because in the AngularJS example the container is shown asynchronously, leading to then .redraw() being fired before the <div /> is shown, nullifying its effect.

This seems to be backed by adding a timeout to the .redraw() call, which yields the correct behaviour:

$scope.toggleMyChart = function () {
  $scope.isMyChartVisible = !$scope.isMyChartVisible;

  if ($scope.isMyChartVisible) {
    $timeout(function () {
      $scope.myChart.redraw();
    }, 10);
  }
};

Demo: http://codepen.io/albireo/pen/jDoew?editors=101

However, this hack adds another problem: a timeout adds an annoying "flash" to the page, the chart first is shown in its "small" state, then disappears for a moment while being rendered and finally reappears with the correct size.

How can I get the desired result (have an hidden chart span the whole available space when being shown) without side effects?

P.s. The problem seems to appear only when the chart is created hidden, if an existing chard is hidden and then shown again it works correctly, see http://codepen.io/albireo/pen/vfzeu?editors=101.

2
Did you ever get to the bottom of this?hally9k
@Hal9K no, we eventually dropped AngularJS because we had to add support for IE < 9, so the problem "no longer existed".Albireo
Your "hack" with redraw when toggling visibility work for me :)aup

2 Answers

2
votes

A bit late maybe - But I found this post looking to solve a similar problem. I just removed the 10 ms delay in your $timeout - which seems to remove the annoying flicker.

I changed this

$timeout(function () {
   $scope.myChart.redraw();
}, 10);

to

$timeout(function () {
   $scope.myChart.redraw();
});

See demo here.

0
votes

For anyone who still has this issue

I'm late in the question but a solution is instead of hiding the chart or canvas, set the opacity to 0 instead of hiding and set it to 1 to reveal

i.e.:

//on page init when hiding
angular.element('#canvas').css('opacity', '0');

//will scale size properly 
angular.element('#canvas').css('opacity', '1');