如何在带有 D3 的 Angular 中使用“this”?

Posted

技术标签:

【中文标题】如何在带有 D3 的 Angular 中使用“this”?【英文标题】:How to use 'this' in Angular with D3? 【发布时间】:2018-03-06 11:38:54 【问题描述】:

Tldr;当 Angular 将 this 绑定到类(组件/服务)时,如何处理引用 D3 对象的 this


我希望在 Angular (v.4) 应用程序中使用 D3.js (v.4)。

我的代码在独立的 javascript 中工作,但我现在需要将它集成到 Angular 应用程序中。

this 的使用让我大吃一惊。

我有一个想要拖动的 SVG 组,所以我使用 .call(drag)

someFunction() 
    this.unitGroup = this.svg.append('g')
            .attr('id', 'unitGroup');
            .call(drag)


当我尝试引用被拖动的 svg 元素时,我的问题就出现了。在我的原始代码中,我可以参考this,例如let matrix = this.getCTM()。在服务中使用此代码时,现在无法使用 this

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

任何关于如何实现这一点或澄清我做错了什么的指针将不胜感激。


我认为这不仅仅是与箭头函数 this 绑定相关的错误,因为 .on('drag', function(d)... 会导致相同的错误。


这是 Plunker 说明我的问题:https://embed.plnkr.co/p1hdhMBnaebVPB6EuDdj/

【问题讨论】:

What does "this" refer to in arrow functions in ES6?的可能重复 改成 on('drag', function (d) ... ) 并不能解决问题。 你能在 plunker 中重现它吗?您可以尝试将this 更改为drag,例如d3.select(drag) 或者您正在寻找d3.event.currentTarget 而不是this 我会看看这样做的。由于需要大量代码,我起初犹豫不决,我希望有人能认出我的错误。 我想在 plunker 中看到它)这是我的 d3 示例 plnkr.co/edit/qM3qrk3swvalQFBh1Db1?p=preview 【参考方案1】:

在大多数 D3 方法中,this 指的是 DOM 元素,它是最简单的获取元素的方法。但是,您在 Angular 代码中使用 this 会遇到一些问题。

好消息是,有一种惯用方法可以在不依赖this(也不依赖d3.event)的情况下获取当前DOM元素:使用second 第三个​​ 参数相结合。这在您无法使用this 的情况下非常有用,例如您现在的情况或使用箭头函数时。

this 的替代方案在 API 中有大量文档。对于大多数 D3 方法,您可以读到该方法是...

... 正在传递当前数据 (d)、当前索引 (i) 和当前组 (nodes),其中 this 作为当前 DOM 元素 (nodes[我])。 (两者都强调我的)

因此,在一个常见的 D3 代码中,您可以使用以下方式获取 DOM 元素:

.on("whatever", function()
    d3.select(this).etc...
//              ^--- 'this' is the DOM element

或者:

.on("whatever", function(d,i,n)
//                         ^-^--- second and third arguments
    d3.select(n[i]).etc...
//              ^--- here, 'n[i]' is also the DOM element

因此,在您的情况下,只需使用:

.on('drag', (d,i,n) => 
    const m = d3.select(n[i]).node().getCTM();
//the same of 'this'-----^
...

由于d3.select(n[i]) 是一个选择,您必须使用node() 来获取实际的DOM 元素。

这是您更新的插件:https://plnkr.co/edit/tjQ6lV411vAUcEKPh0ru?p=preview

【讨论】:

太棒了!谢谢! Plunker 按预期工作。为了让它在我的代码中工作,我必须做一个小的调整来让一个打字错误通过——“元素”类型上不存在属性“getCTM”。 const m = d3.select(n[i] as any).node().getCTM(); 有趣。在我的AppComponent 中声明d3 时,如何在不声明this.d3 的情况下引用d3?就我而言,我在 AppComponent 的 ngOnInit 上创建了画布,我似乎无法在不声明 this.d3 的情况下引用 d3 @MichaelArdan 抱歉,我不是 Angular 用户。 非常有帮助,谢谢this - 以编程方式同时使用 angular 和 d3 真的很痛苦。 非常感谢!对于面积图,我也面临同样的问题,但这种方法解决了我的问题。【参考方案2】:

尝试使用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

【讨论】:

感谢您的解决方案!我已将@GerardoFurtado 的答案标记为已接受,因为它看起来更可靠。在您的解决方案中移动深灰色框时,事件有时会出现切换到较大的框然后移动它。 它解决了其中一个问题!我很感激你的帮助。下一个问题可获得的奖励积分:***.com/questions/46423055/…

以上是关于如何在带有 D3 的 Angular 中使用“this”?的主要内容,如果未能解决你的问题,请参考以下文章

使用 D3 在 DOM 中动态添加/删除 Angular 组件

D3、TS 和 Angular 2

Angular 通过另一个组件获得新的范围

Angular - 如何将整个垫脚行移动到表格顶部

如何将 D3.js 与 Renderer API 与 Angular 2 集成

如何在使用 Angular js 显示数据时进行分页?