Binding Data to Charts using Vue Components and D3
Tags: vue, d3, javascript, frontend
Published: 2018-08-30 03:40:57

---
title: Binding Data to Charts using Vue Components and D3
published: true
description: In which we use D3 to create a chart component in Vue
tags: vue, d3, javascript, frontend
cover_image: https://thepracticaldev.s3.amazonaws.com/i/85cvuxwyd0qqfdifv99u.jpeg
---

There is nothing more satisfying than making a change and seeing that change propagated through somewhere immediately.  Lately, I have been experimenting a lot with integrating D3 charts into Vue.  I tried doing this with [helpers](https://github.com/ignoreintuition/d3vue), directives and, most recently, with [components](https://github.com/ignoreintuition/v-chart-plugin).  Since Vue is declaring components to be their recommended implementation method for reusable components I am going to be focusing here on that version.  

If you are unfamiliar with [Vue](https://vuejs.org/) it is a popular JavaScript framework for two-way data binding.  [D3](https://d3js.org/) is a library for modeling data in the DOM.  What we are going to be focusing on is building a reusable component in Vue that leverages the D3 library to create a chart stays in sync with the data in Vue.

We are going to start with the [vue-cli](https://www.npmjs.com/package/vue-cli) which will allow us to create a template for our new app.  Once we have our starter app we are going to create a JavaScript file within our src directory called v-chart-plugin.js.  This will need to be imported and used in our main.js

```javascript
import Chart from './v-chart-plugin.js'

Vue.use(Chart);

```
The idea here is that all of the functionality for rendering the chart and binding the data will be handled within the component completely seperate from the Vue instance.  This way when the component is added all the developer needs to do is add in the element with the proper data bindings.

```HTML
<v-chart v-bind:chartData="chartData"></v-chart>
```
As long as the object (in this case chartData) being passed follows the appropriate structure with the required values there is nothing further required of the dev.  The component itself will be structured as so:

* Import dependent libraries (D3)
* Define the component
* Export the component
* Use the component (if vue is defined at the global scope)

The framework would look something like this

```javascript
// Limited to just those dependencies we are going to need for the example
var d3 = Object.assign({}, 
    require("d3-selection"));

const Chart = {
...
}

export default Chart;

if (typeof window !== 'undefined' && window.Vue) {
    window.Vue.use(MyPlugin)
}
```
The bulk of the plugin is going to be in the definition of the Chart object itself.  If you are familiar with Vue then you should recognize a lot of these methods and properties.  If you are familiar with other frameworks like React and Angular some of the terminology should be familiar as well.  Let's examine the structure of the component before we get into the details


```javascript
const Chart = {
    install(Vue, options) {
        Vue.component('v-chart', {
            props: ['chartData'], 
            data: function () {
                ...
            },
            methods: {
                ...
            },
            // lifecycle events
            mounted: function () { 
                ...
            },
            // watch functions
            watch: {
                ...

            },
            template:
                ...
        })
    }
}
```

You should recognize the value v-chart.  This is the value that was used in our Vue template to add the component to the application.  Below that we are referencing the props.  This correlates to the object that we bound via the v-bind directive.  You can pass multiple parameters instead of an object but I'll be using an object as it is easier for configuration.  Following the props are the familiar data and methods which are values and functions that are scoped to the component.  We won't be using data in this example as all the data is coming from the props but we will be using methods extensively.  Note that for components data needs to be a function.

```javascript
Vue.component('v-chart', {
    props: ['chartData'], 
    data: function () {
        return {
        }
    },
    methods: {
        initalizeChart: function () {
            this.drawChart();
        },
        refreshChart: function () {
            this.clearCanvas();
            this.drawChart();
        },
        drawChart: function () {
            ...
        },
        clearCanvas: function () {
            d3.select(this.chartData.selector).selectAll("*").remove();
        },
        getHeight: function () {
            return this.chartData.height || 200;
        },
        getWidth: function () {
            return this.chartData.width || 200;
        }
    },
```

Outside of our methods we have a few additional functions that serve some specific purposes.  Firstly are the [lifecyle hooks](https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram).  These are functions that get called at specific intervals within the application.  This allows us to associate functionality with these events such as when an instance is created, updated, destroyed, etc.  We'll be using the `mounted` hook to ensure the page is loaded when we render our chart.

![Lifecycle Hooks](https://thepracticaldev.s3.amazonaws.com/i/gowj25um09ko6yrvfpqz.png)

```javascript
mounted: function () { // <-- lifecycle events
    this.initalizeChart();
},

```

The other special methods we will be adding are watch functions.  Watch functions serve the unique purpose of executing when the data is updated.  This will allow us to re-render the chart when the data changes.  Doing so will ensure that the chart is always in sync with the data.

```javascript
watch: { // <-- watch functions
    'chartData': {
        handler: function (val) {
            this.refreshChart();
        },
        deep: true
    }
},

```
Lastly, we have the template.  This is a standard Vue template for the content we intend on rendering on the page.  In this example, it is simply an SVG as that is what we will be using to draw our graph.  We use interpolation to get the values of the width and height based on what was configured in the object.

```javascript
template:
    `<svg class="chart" :height="this.getHeight()" :width="this.getWidth()"> </svg>`

```

If you have been following along you will notice I purposefully left out the details of the drawChart function.  This is the part of the code that uses D3 to draw the chart on the SVG canvas we created in our template.  This is going to rely heavily on the data we are passing in from the props: specifically the chartData.selector for identifying the unique id for the chart and chartData.data which is an array of data configured in the Vue instance.  The rest is a boilerplate  D3 that binds the data and appends rectangles with a length equal to each value in the array.  This can be extended to create any data visualization.

```javascript
drawChart: function () {
    d3.select(this.chartData.selector)
        .append("text")
        .attr("x", 20)
        .attr("y", 20)
        .style("text-anchor", "left")
        .text(this.chartData.title)

    d3.select(this.chartData.selector)
        .selectAll("g")
        .data(this.chartData.data)
        .enter().append("g")
        .append("rect")
        .attr("width", function (d) {
            return d;
        }).attr("height", 20)
        .attr("y", function (d, i) {
            return (i + 1.5) * 20 + i
        }).attr("x", 0);
},
```
![screenshot](https://thepracticaldev.s3.amazonaws.com/i/7bu2p3740cfn3cq3qygm.png)

If you want to see the full code go ahead and clone the [Github Repo](https://github.com/ignoreintuition/v-chart-plugin) that this tutorial was based on.  Hopefully, this gives you a foundation on how to bind your data from Vue with your components and model it in visualizations.