Reactive Charts in Angular 8 using D3.js

Key aspects necessary for creating production-ready code

Rajaram Gurumurthi
The Startup
8 min readOct 9, 2019

--

Real-time pizza delivery lead-time frequency distribution (fictional data)

This article shows you how to build reactive charts inside an Angular 8 application using the D3 JavaScript framework.

While there are a number of good articles and discussion threads covering this topic (some listed in the references), I have attempted to bring together all the key aspects necessary for creating production-ready code.

Why D3 on Top of Angular?

D3’s (Data-Driven-Documents) core capability is to manipulate DOM elements in response to dynamic application data. But, so it is for Angular, React, jQuery, and a host of other frameworks.

The motivation to use the D3 (or similar) framework with Angular is to provide the following data visualization capabilities:

  • Dynamically creating and removing graphical elements from the DOM.
  • Binding application data to graphical elements.
  • Transforming user data to chart and shape coordinates.
  • Animating graphical elements using transitions and interpolation.
  • Advanced mathematical functions.

Given these D3 capabilities and lack of equivalent features in Angular, we proceed with mixing the two with an abundance of caution.

Collaborate Without Competing

Angular frowns upon direct DOM manipulation using the native DOM API or other frameworks. There is a risk of breakage when application code directly acts on a DOM element that is already being modified by Angular.

Designing the components to isolate the sections of the DOM managed by Angular vs. D3 mitigates some of the risk.

The parent component delivers the chart peripherals and hosts the chart component inside one dedicated container.

Everything inside that container is then managed by the child component using D3 alone. This eliminates the possibility of both frameworks trying to modify the same DOM element (except in the case of a coding blunder!).

Installation

The code and techniques in this article apply to the following framework versions.

  • Angular 8.2.9
  • D3 5.12.0
  • TypeScript compiler target: es2015

Review package.json and tsconfig.json in the project repository for the complete set of dependencies and options.

Assuming you have already created an Angular 8 application, do the following in the project home directory:

Code

Create the chart component

Create an Angular component to host the chart functionality.

Import D3 into this component.

Accept input data

D3 charts can accept data in various shapes. However, all our example area chart needs is a few arrays of numbers, each representing a component of delivery lead times for a fictional pizza franchise.

Pre-process data

The first step in receiving data is to transform it to chart/shape parameters.

In our area chart example, this involves taking the raw lead times, converting them first to a frequency distribution and then to a chart area path element (a set of points that covers the area with color and other information).

D3 provides functions to do this for each type of chart (d3.pie(), d3.histogram, d3.area(), and so on).

Create the chart elements

Using D3, add SVG and other graphic elements to the DOM.

Make it responsive

Incorporate the following to make the chart responsive to device dimensions and orientation:

  • Use the SVG viewBox attribute while creating the SVG element.
  • Specify all dimensions for elements inside the SVG in relation to the viewBox width and height.
  • Ensure that the hosting container in the parent component is responsive.

Make it reactive

Input can be accepted into a component instance variable with the @Input decoration. The chart can react to data changes injected by the host component by implementing the Angular OnChanges interface.

Once a change is detected, receive and reprocess the raw data using the same D3 histogram function used for creating the chart and bind the data to existing area paths. Add animation for a smooth transition effect.

While the above is sufficient to update an area chart with smooth transition, animating other chart shapes can be more involved.

More on animation

Another example in the demo is a donut chart that shows the count of orders in each status.

The pie and donut charts have their own pre-processing function. We create the charts using a similar approach as the earlier example:

Once new data arrives, rebind the data to the pie slices and related elements to update the chart.

The animation of pie/donut charts requires “interpolation”. Given the shape data for the current and future state after update, the D3 “tween” functions help compute a set of intermediate shape data sets.

This is called interpolation. The intermediate data sets (interpolations) are used by the D3 transitions to morph the chart element slowly from its current state on its way to the target state.

The label tweening function interpolates the intermediate centroid positions, based on previous and current raw data values.

Note: D3 provides various shape functions to convert user data to shape data, e.g. pie, histogram, area, etc. Different functions apply to different chart types. As is the case with tween functions.

Create the parent component

Develop the parent component using Angular.

Do not mix Angular and D3 DOM manipulation within the same component (my recommendation).

Keep the chart peripherals (title, legends, and data tables) in this parent component for the following reasons:

  • Easier to implement the peripherals in Angular.
  • Maintains visual harmony with the overall app look and feel.
  • Maintains separation of concerns between Angular and D3.
  • D3 does not have native support for generating chart legends.

Provide a container in the host component and embed the chart component as a child. Do not place any other elements within this container.

Create a handle for the child component using ViewChild. Ensure ViewEncapsulation in the child component is set to None, to allow global style classes to SVG elements.

Note: A previous version of this article mixed up the Component decorators for the parent OrderDeliveryComponent and the child AreaChartComponent. The ViewEncapsulation attribute is to be set in the decorator for child component as shown now.

Style the chart container

Ensure that the chart container has height and width attributes. The child component can make use of these to size the chart and make it responsive.

If the chart component uses the viewbox attribute on the SVG element and is set with 100% height and width, the resulting chart will automatically scale or shrink in harmony with its parent.

Prepare data and pass it to the chart component

Conclusion

D3 provides a powerful API for DOM manipulation. Using it for data visualization within an Angular application can greatly enhance the user experience.

However, risks of mixing the two frameworks need to be mitigated by separation of concerns, as described in this article.

--

--