Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
154 views
in Technique[技术] by (71.8m points)

javascript - How to use 'this' in Angular with D3?


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/

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...