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

断了今生、忘了曾经 提交于 2019-12-01 18:33:16

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))
);

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!