Typescript/D3 v4 - Context of this in d3.drag().on(“end”, this.dragended)

后端 未结 3 645
后悔当初
后悔当初 2021-01-18 07:46

I am using the D3 library to move an item within a Venn-diagram. When I stop dragging I want to determine the position of the item in the diagram.

item.call(         


        
相关标签:
3条回答
  • 2021-01-18 08:35

    If you need to keep the reference to the class instance and also the element instance referenced by d3 drag you can define your listener functions as:

    export class MyClass {
        @Input()
        radius: number = 45;
    
        constructor() {
            d3.drag()
              .on("start", this.dragStarted(this))
              .on("drag", this.dragged(this))
              .on("end", this.dragEnded(this));
        }
    
        private dragged(self) {
            return function(d) {
                // 'this' in this context will be the d3 element
    
                d3.select(this)
                  .attr("cx", d.x = self.radius * Math.cos(alpha))
                  .attr("cy", d.y = d3.event.y < 0 ? -self.radius * Math.sin(alpha) : self.radius * Math.sin(alpha));
            }
        }
    
        ...
    
    }
    

    Tested with d3.js v4

    0 讨论(0)
  • 2021-01-18 08:45

    As a matter of principle, D3 binds the this context of callbacks on selections, transitions and the like to the DOM element on which they operate.

    By implication, if you need to access the this context of a "wrapping object" in a lexical scope sense, you cannot use this to access it. Applied to your specific case:

    • d3.select(this) will operate on the currently iterated DOM element which will have the type of the DOM element underlying the item selection. So, if your item is e.g. of type SVGCircleElement, such will be the this context of your dragended function.
    • A way to accessing the wrapping objects this context as well is by creating a closure around the dragended function:

    You can add a private method to your wrapping object which has the save method:

    private getDragEndedHandler() {
      let self = this; // object context to preserve
      return function(d: TCMemberScenario, i: number) {
        d3.select(this).classed("active", false); // D3 bound DOM element context
        d.calculateRoles();
        self.save(); // closure-preserved object context
      }
    }
    

    Now, when you bind the handler event, you simply do the following:

    item.call(d3.drag()
        .on("start", this.dragstarted)
        .on("drag", this.dragged)
        .on("end", this.getDragEndedHandler())
    );
    

    The same pattern can be applied to the other event handlers if need be.

    If you use the latest definitions for d3-selection and d3-drag from npm @types, you will see that they now have generics to be more explicit about the this context typing pertaining to DOM elements and D3 callbacks.

    0 讨论(0)
  • 2021-01-18 08:53

    To keep reference to 'this' use arrow functions like this:

    item.call(d3.drag()
        .on("start", (d, i) => this.dragstarted(d,i))
        .on("drag", (d, i) => this.dragged(d, i))
        .on("end", (d, i) => this.dragended(d, i))
    );
    
    0 讨论(0)
提交回复
热议问题