cannot integrate d3 into Vue 3 Component

Viewed 782

I've installed the d3 dependency in the project directory like this:

npm install d3 --save

and I've imported the d3 module in the Component like this:

import d3 from "d3";

which shows no errors or warnings to me. Unfortunately, calling any method from the d3 module fails.

For example, I tried to draw this line chart and an error appeared at the very first select method from the module.

Uncaught (in promise) TypeError: Cannot read property 'select' of undefined.

How could this be? Hovering on every single method of the d3 module in VS Code shows me the documentation of the method. So I guess the module can't be undefined.

<script>
import d3 from 'd3';
const data = [99, 71, 78, 25, 36, 92];
export default {
  name: 'non-vue-line-chart',
  template: '<div></div>',
  mounted() {
    const svg = d3.select(this.$el) // the error appears on the very first line of the d3-module
      .append('svg')
      .attr('width', 500)
      .attr('height', 270)
      .append('g')
      .attr('transform', 'translate(0, 10)');
    const x = d3.scaleLinear().range([0, 430]);
    const y = d3.scaleLinear().range([210, 0]);
    d3.axisLeft().scale(x);
    d3.axisTop().scale(y);
    x.domain(d3.extent(data, (d, i) => i));
    y.domain([0, d3.max(data, d => d)]);
    const createPath = d3.line()
      .x((d, i) => x(i))
      .y(d => y(d));
    svg.append('path').attr('d', createPath(data));
  },
};
</script>

There must be some integration mismatch, that I can't fix it by myself. Please help.

1 Answers

Would be helpful to know which version of d3 you are using. I'm going to assume it's the latest, which at this time is v6.

d3 v4 still uses import d3 from 'd3'; way to import, but in v6 (and v5) the use ES modules now and way to do is.

import * as d3 from 'd3';

This change allows the developer to select which features they need, and the compiler can then tree-shake the dependency and only include features needed.

So you could also do:

import {select, scaleLinear, axisLeft, axisTop, extent, max, line} as d3 from 'd3';

and end up with a smaller bundle


Update

The error: Uncaught (in promise) DOMException: Failed to execute 'appendChild' on 'Node': This node type does not support this method. is telling you that $el in d3.select(this.$el) is not a Node. To resolve this, use inside this.$nextTick(function () {...})

docs

mounted

Type: Function

**Details: **

Called after the instance has been mounted, where element, passed to app.mount is replaced by the newly created vm.$el. If the root instance is mounted to an in-document element, vm.$el will also be in-document when mounted is called.

Note that mounted does not guarantee that all child components have also been mounted. If you want to wait until the entire view has been rendered, you can use vm.$nextTick inside of mounted:

mounted() {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
  })
}
Related