New AngularJS Charts

With Build 1.1.0 of our AngularJS directive (GitHub or npm), we added a new $scope.on('destroy',function()) to help clean up your charts in memory. Why the sudden change? We found many users run into issues when cleaning up charts while switching between views. Switching views without cleaning up the charts can cause unwanted behavior, such as unresponsive interactions, rendering issues, and occasional console errors.

Why does this happen? ZingChart lives outside of the Angular scope. When rendering and binding events to ZingChart, you are working outside of Angular. When switching views, DOM elements are destroyed, but ZingChart lives on. This can leave "zombie" charts floating around your application.

Let's illustrate this with a popular use case. We have several navigation tabs and a view that changes based on the tab your user clicks.

  <!-- Navigation tabs -->
  <header>
    <a href="#/">Home</a>
    <a href="#view1">View 1</a>
    <a href="#view2">View 2</a>
  </header>
  <h1>This content is static</h1>

  <!-- Views Alternating here -->
  <div class="view-contents" ng-view></div>

You can use the Angular router to define what view to load.

// define routes
app.config(function($routeProvider) {  
  $routeProvider
    .when('/view1', {
      templateUrl : 'view1.html'
    })
    .when('/view2', {
      templateUrl : 'view2.html'
    })
    .otherwise({
      template : '<h3>Nothing Exists In This View</h3>'
    });
});

When you click on a tab, the current view is destroyed. In doing so, all of the DOM elements are destroyed. If you had ZingChart bound to a DOM element in that view, it too would be destroyed. Here is where the problem occurs. ZingChart is in the global scope outside of Angular, and so the reference to the chart still exists, but its coexisting DOM element has now been destroyed. When the new view is rendered, you can still execute calls on that chart because the data still persists in the global ZingChart object running at the application level.

That the chart still exists can be verified with the following API call to getdata.

zingchart.exec('chart-1','getdata')  
  • Navigate to View 1, and then View 2. The intended behavior is on View 2: the previous chart JSON will be spit out on the page.

Disclosure: If clicking on view 1 does not render a chart, plnkr has failed to load a certain resource. Refresh the plnkr with the button in the top right to alleviate this.

To reiterate, this is unwanted because the view has changed and the DOM element related to the chart has been destroyed. Yet we can still access that data.

How To Remedy This?

You can add the $scope.on('destroy',function()) to your current directive, which will clean up the charts by calling on zingchart.exec('chart-1','destroy') to get rid of the global reference to that chart. This is how it looks in our controller.

// define controller
app.controller('MainController', function($scope, $timeout) {  
  ...
  // destroy the chart
  $scope.$on('$destroy', function() {
    zingchart.exec('chart-1', 'destroy');
  });
});
  • Once again, navigate to View 1, and then View 2. View 2 should always display: Contents Undefined. This is what we want!

Disclosure: If clicking on view 1 does not render a chart, plnkr has failed to load a certain resource. Refresh the plnkr with the button in the top right to alleviate this.

Wrapping Up

Some projects have many charts, which can make cleanup a lot of work. You have to keep track of the chart ids on the page and properly destroy them. Managing this on your own can be tedious and clutter up your code. In examples like the ones above, it is easy to add your own code. But for the general case, we have updated the directive here on GitHub and here at npm so that when switching views, ZingChart now cleans up the charts for you!

  • So for one last time, navigate to View 1, and then View 2. View 2 should always display : Contents Undefined.

Disclosure: If clicking on view 1 does not render a chart, plnkr has failed to load a certain resource. Refresh the plnkr with the button in the top right to alleviate this.