While building my senior capstone project, iSprinkle, I needed a way to display a time series graph of data. A working demo can be found on my Codepen and a gist over on Github. Here’s what I learned.

Implementing a time series graph into an existing Angular app requires a few dependecies:

  • D3, a data visualization library
  • NVD3, a reusable chart library for D3
  • Angular-nvD3, for AngularJS directives
  • Moment.js, a library for working with JavaScript dates/times

Note: As of this writing, nvd3 supports D3 versions 3.5.3 and above, but not D3 4.x .

In this minimal example, our Angular app and controllers are named like so:

	<div ng-app="nvd3_timeseries">
		<div ng-controller="demo">
		<nvd3 options="options" data="data"></nvd3>

However, because some dependecies depend on others, we’ll need to make that they load in a particular order:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.16.0/moment.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.4/nv.d3.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.4/nv.d3.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-nvd3/1.0.9/angular-nvd3.min.js"></script>

Finally, most of the code for the graph is found within the home controller. In $scope.options, we’ll find the configuration options for the chart. The actual data for the chart is stored in $scope.data, which should contain one or more time series objects in this format:

// xy_coordinate_pairs is a time series and should be an array of x, y coordinate pairs
// {x: xVal, y: yVal }
return [{
  values: xy_coordinate_pairs,
  key: 'Chart Key',
  color: '#1b75ba'

In this example, the generateTimeSeries function is called to

// 1) Declare app
var nvd3_timeseries = angular.module('nvd3_timeseries', ['nvd3']);

// 2) Declare controller
nvd3_timeseries.controller('demo', ['$scope', function($scope) {

    // 3) Chart configuration
    $scope.options = {
        chart: {
            type: 'lineChart',
            height: 450,
            margin: {
                top: 20,
                right: 20,
                bottom: 40,
                left: 55
            x: function(d) {
            Moment.js will attempt to parse the x coordinate 
            and return a value in milliseconds
                return moment.utc(d.x).valueOf();
            y: function(d) {
                return d.y;
            useInteractiveGuideline: true,
            xAxis: {
                axisLabel: 'Date',
                tickFormat: (function(d) {
                    // 3.1) Format the x ticks
                    return d3.time.format('%-m/%-d/%-Y')(new Date(d));
            yAxis: {
                axisLabel: 'Cats',
                axisLabelDistance: -10
            callback: function(chart) {}
        title: {
            enable: true,
            text: 'Time Series graph'

    $scope.dateRange = function dateRange(startDate, endDate) {
        // http://stackoverflow.com/a/23796069
        var dates = [];

        var currDate = startDate.clone().startOf('day');
        var lastDate = endDate.clone().startOf('day');

        while (currDate.add(1, 'days').diff(lastDate) <= 0) {

        return dates;

    // 4) Chart data
    $scope.data = generateTimeSeries();

    function generateTimeSeries() {
        // Generate an array of dates for plotting
        var dateRange = $scope.dateRange(moment().subtract(7, 'days'), moment());

        // Plottable will be an array of (x, y) objects
        var plottable = dateRange;

        plottable.forEach(function to_utc(date, index) {
            console.log('a[' + index + '] = ' + date);
            // set x value to date and y to a random value
            var rand = Math.round((Math.random() * (100 - 1) + 1));
            var point = {
                x: date,
                y: rand
            // Replace original element
            plottable[index] = point;

        // Return the plottable array for plotting
        return [{
            values: plottable,
            key: 'Cats',
            color: '#1b75ba'

As an aside, although this chart is initiated with data instantly, we can set an empty data value like so:

$scope.data = [{
    "key": "Some key",
    "values": []