Reputation: 119
I am working on Vue project with D3.js, TypeScript and Vue Property Decorator. I wanted to draw a heatmap but I get an error at when I wanted to call x()
or y()
function in return for a computed position of each cell in the heatmap. It throws the error
Type "SVGAnimatedLength" has no call signatures.
This is how I init my chart variables
private svg: d3.Selection<SVGGElement, any, HTMLElement, any>
private x: d3.ScaleBand<string>
private xAxis: d3.Selection<SVGGElement, any, HTMLElement, any>
private y: d3.ScaleBand<string>
private yAxis: d3.Selection<SVGGElement, any, HTMLElement, any>
this is where it causes error
this.svg.selectAll()
.data(this.getData().filter((datum: HeatmapData) => this.betweenTwoDates(datum)))
.enter()
.append('rect')
.attr('x', function(d: HeatmapData) {
return this.x(d.dayNumber)
})
.attr('y', function(d: HeatmapData) {
return this.y(d.timeOfDay)
})
.attr('cx', 1)
.attr('cy', 1)
.attr('width', this.x.bandwidth())
.attr('height', this.y.bandwidth())
at
.attr('x', function(d: HeatmapData) {
return this.x(d.dayNumber)
})
the error happens at return this.x(d.dayNumber)
stating Type "SVGAnimatedLength" has no call signatures
. Same goes for .attr('y', ...)
too.
The this
at this.x()
has a type of SVGRectElement
.
Upvotes: 1
Views: 273
Reputation: 1931
The this
has a type of SVGRectElement because it is inside a regular anonymous function. In D3, methods for manipulating nodes replace the this
context with the own DOM Element that is being manipulated, in your case a <rect>
. The <rect>
nodes don't have x or y methods, hence the type error.
Replacing the anonymous functions with arrow functions preserves the this
from the outside:
.attr('x', (d: HeatmapData) => {
return this.x(d.dayNumber)
})
.attr('y', (d: HeatmapData) => {
return this.y(d.timeOfDay)
})
Now, the this
inside the function is the same this
from the outside, which in your case is the class that contain svg, x, xAxis, y and yAxis.
Upvotes: 1
Reputation: 21578
This is a nice example of when to actually use arrow functions in favor of regular functions! Because regular functions—like in your code—establish their own this
scope you no longer have access to the this
scope you are interested in, namely the scope of your surroungding class.
Many D3 methods are called with this
set to the current DOM element:
with this as the current DOM element (nodes[i])
To be able to use your class instance's methods by referring to them using this
you can just use an arrow function which does not come with its own scope but captures the scope of its surrounding context, i.e. your class/instance. Your methods should therefore look like this:
.attr('x', d => this.x(d.dayNumber))
Upvotes: 3