Tldr; How do I deal with this
in reference to a D3 object when Angular binds this
to the class (component/service)?
I am looking to use D3.js (v.4) in an Angular (v.4) app.
My code works in standalone JavaScript but I now need to integrate it into an Angular app.
The use of this
is tripping me up.
I have an SVG group that I wish to drag and so I use .call(drag)
someFunction() {
this.unitGroup = this.svg.append('g')
.attr('id', 'unitGroup');
.call(drag)
}
My problem comes about when I try to reference the svg element that is being dragged. In my original code, I could refer to this
e.g. let matrix = this.getCTM()
. Using this
is now not working when using this code within a service.
drag = d3.drag()
.on('start', () => {
this.setClickOrigin(d3.event);
})
.on('drag', (d) => {
const m = this.getCTM(); // <--- PROBLEM HERE
const x = d3.event.x - this.clickOrigin.x;
const y = d3.event.y - this.clickOrigin.y;
this.setClickOrigin(d3.event);
d3.select(this) // <--- PROBLEM HERE
.attr('transform', `matrix(${m.a},${m.b},${m.c},${m.d},${m.e + x},${m.f + y})`);
});
Any pointers on how to implement this or clarification of what I am doing wrong would be appreciated.
I don't think this is simply an error associated with the arrow function this
binding as .on('drag', function(d){...}
results in the same error.
Here is Plunker illustrating my issue: https://embed.plnkr.co/p1hdhMBnaebVPB6EuDdj/
D3 natively supports JSON data, so it makes it really easy to integrate with your Angular application. First, you'll need a JSON API endpoint or file.
The Data-Driven Documents (D3) library is one of the most popular libraries for producing interactive charts. There are many samples of charts available on the Internet, which can be reused in an Angular application.
The JavaScript ecosystem has completely changed during this time, in terms of libraries, best practices and even language features. Nevertheless, D3 is still here. And it's more popular than ever.
We have compiled a list of solutions that reviewers voted as the best overall alternatives and competitors to D3js, including Syncfusion Essential Studio Enterprise Edition, Chart. Js, Angular, and DevExpress.
In most of D3 methods, this
refers to the DOM element, and it is the most simple way to get the element. However, you're facing some problems using this
in your angular code.
The good news is that there is an idiomatic way to get the current DOM element without relying on this
(and without relying on d3.event
as well): using the second and the third arguments combined. This is quite useful in situations where you cannot use this
, like your situation right now or when using an arrow function, for instance.
That alternative to this
is extensively documented on the API. For most D3 methods, you can read that the method is...
... being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]). (both emphases mine)
So, in a common D3 code, you can get the DOM element using:
.on("whatever", function(){
d3.select(this).etc...
// ^--- 'this' is the DOM element
Or:
.on("whatever", function(d,i,n){
// ^-^--- second and third arguments
d3.select(n[i]).etc...
// ^--- here, 'n[i]' is also the DOM element
Therefore, in your case, just use:
.on('drag', (d,i,n) => {
const m = d3.select(n[i]).node().getCTM();
//the same of 'this'-----^
...
}
Since d3.select(n[i])
is a selection, you'll have to use node()
to get the actual DOM element.
Here is your updated plunker: https://plnkr.co/edit/tjQ6lV411vAUcEKPh0ru?p=preview
Try using d3.event.sourceEvent.target
:
.on('drag', () => {
const target = d3.event.sourceEvent.target;
const m = target.getCTM();
const x = d3.event.x - this.clickOrigin.x;
const y = d3.event.y - this.clickOrigin.y;
this.setClickOrigin(d3.event);
d3.select(target)
Forked Plunker Example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With