Our world is based on data. We gather it everywhere: forms, feedback, learning techniques, data mining, etc. When it comes to working with that data, we need to do more than show numbers back to our users; we need to make it easy for them to understand what the numbers mean.
By combining the D3 data visualization tool and the Angular cross-platform application development platform, we can take that data and create versatile and interactive visualizations that respond to dynamic data. This squeezes that last 10% out of your data and takes your data-based application to the next level for your users.
What is D3?
D3.js is an open source JavaScript library that provides powerful manipulation of the Document Object Model (DOM) driven by data. It gives you all the tools you need to create any visualization you can imagine. You get data transformations to prepare the data, shape creation for visualizing the data, layouts to transform the data into different representational layouts, transitions to give visual flair as the data and shapes change, and powerful tools for interaction. It's based on web standards, allowing you to transform your data and give it life with HTML, CSS, and SVG.
Getting started with D3 is easy. To create a visualization, we first need an SVG element to work with:
var width = 500,
height = 500;
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ') rotate(-90 0 0)');
Let's have a little fun creating a sunburst. Think of it like a multi-level donut chart, great for displaying hierarchical data. (It’s also more fun than a simple bar chart example.) To get started, we need a generator for our shape, in our case an arc. D3 provides a generator to create a circular sector that you can customize.
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, xScale(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, xScale(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, yScale(d.y));
})
.outerRadius(function(d) {
return Math.max(0, yScale(d.y + d.dy));
});
Now we're ready for the data.
General update pattern
D3 has a pattern for how to deal with your data. The pattern is join, enter, update, and exit. This involves a data join for merging new and existing data, followed by the entering, updating, and removing of data. The pattern is easy with D3 using data(), append(), transition(), and exit().
/* JOIN new data with old elements */
var gs = svg.selectAll('g')
.data(partition.nodes(root));
/* ENTER new elements present in new data */
var g = gs.enter().append('g')
.on('click', click)
.on('mouseover', mouseoverArc)
.on('mousemove', mousemoveArc)
.on('mouseout', mouseoutArc);
var path = g.append('path');
/* UPDATE old elements present in new data */
gs.select('path')
.style('fill', function(d) {
return color((d.co ? d.co : 'base'));
})
.transition().duration(500)
.attr('d', arc)
.each(function(d) {
this.x0 = d.x;
this.dx0 = d.dx;
});
/* EXIT old elements not present in new data */
gs.exit()
.transition()
.duration(500)
.style('fill-opacity', 0)
.remove();
By putting this all together with some data, D3 will do the heavy lifting of creating our sunburst chart. We even have functions set up to provide interactivity (see the ENTER section of the code). But what if we want this chart to communicate with other charts? Enter Angular components.
What is Angular?
Angular is a platform for building mobile and desktop web applications. It's an opinionated framework made for building applications, giving you all the pieces you need. It's a lot of things, but we're going to concentrate on one piece: components. They're the base building block of the UI and the Angular code that will interact with our D3 code.
With our powers combined
The two powerful pieces of the component that enhance D3 visualization are inputs and outputs. Inputs allow our components to receive data, meaning we can have a component to generate our sunburst that knows nothing about how to get the data, but still will render it when it comes in. Outputs allow our components to pass data back up to parent components, communicating events and data back up the stack.
@Input()
sunData: dataPoint[];
@Input()
filters: string[];
@Output()
onFilter: EventEmitter<string[]> = new EventEmitter<string[]>();
Angular uses both decorators and TypeScript in its code, which adds a lot of value and power to your code. The property decorators of @Input and @Output define inputs and outputs to our component. By defining the property this way, the Angular compiler automatically creates a binding to the property and links it. On the input, we can assign a type to our data. It creates better code by defining what data it expects, which will come in handy when you connect the component to other code within your Angular application. Angular can also optimize components with different change-detection strategies. You can tell a component to update itself only on push, where it will look at the input's reference and update only if it changes.
On our output, we use an EventEmitter. This gives our component the ability to emit data to a function provided to our component. We can connect this to our visualization and the click event. As we click points, we can pass values out to our containing component, such as a DashboardComponent. The DashboardComponent can then update the data either by filtering the data or querying an API. Our D3 sunburst component and any others we create will see the change, and the general update pattern will take over.
Open source to the future
One of the great parts of working with both Angular and D3 is they are both open source and provide a wealth of examples from each community. For example, D3 has a great community with a plethora of charts, from the basic through the imaginative. They can act as inspiration for your data or as jumping-off points to help you start creating that big idea.
To learn more, attend John Niedzwiecki's talk, "D3 + Angular = Visual Awesomesauce," at Connect.Tech, September 21-22, 2017, in Atlanta.
7 Comments