【问题标题】:Simplest way of integrating Angular-created elements with D3将 Angular 创建的元素与 D3 集成的最简单方法
【发布时间】:2014-06-07 12:51:42
【问题描述】:

在学习这两个框架的背景下,我正在尝试集成 D3 和 Angular 的不同方式,并希望得到一些意见:

我的客户端应用程序从我的服务器接收到一个 JSON 节点数组和一个边数组。 客户端的中心组件是实现为 D3 力导向布局的图形可视化:对于节点数组中的每个节点,将一个 (SVG) 圆形元素添加到此可视化中,并为每个边添加这些圆之间的线边缘数组。

当然,D3 的selection.data( ) 使得添加这些元素并将每个元素绑定到它所代表的数据变得微不足道,但是图形可视化只是一个更大的应用程序的一部分:我需要创建不同类型的元素来表示这些相同应用程序不同部分的节点和边(与 D3 无关),我想将所有这些元素绑定到单个数据集。

我的主要目标是最大限度地降低代码复杂性,并且 - 尽管我已经爱上了 D3 数据绑定功能的简单性和优雅性 - 我得出了一个初步结论,即在应用程序的一部分中使用它,同时使用 Angular 在其他部分做同样的事情会造成不必要的复杂性,最简单的方法是使用 Angular 来处理数据绑定/元素创建

换句话说,我认为我应该使用ng-repeat="node in nodes" 来创建SVG 元素,而不是使用selection.data( ).enter( ).append( ),也许将每个创建为自定义指令以允许自定义功能。完成后,我需要将这些元素“放入”D3,即由其强制导向布局管理。

我的推理合理吗? 特别是,我担心我忽略了这将在对象恒定性方面产生的复杂性,这是一个重要的要求,因为节点将进入,退出并不断在屏幕上移动,我需要这些过渡是平滑的。你会建议我如何将我的角度创建的元素集成到 D3 中(更准确地说,将它们放入我的 force.nodes{ }force.links( ) 数组中)以避免任何此类并发症?

最后,我还在考虑一种策略,希望它能给我带来两全其美的效果:我可以使用 D3 创建我的 SVG 元素并将它们绑定到各自的数据,而不是在可视化指令的link 函数(正如我一直在做的,正如我发现的所有 Angular/D3 教程所做的那样),我可以在 compile 函数中运行它,并执行以下操作:

d3.select('SVG')
  .selectAll('.node')
  .data('nodeDataArray')
  .enter( )
  .append('circle')
  .attr("class", "node-icon"); //...other attributes/styles etc  

其中node-icon 是具有restrict 属性的指令的规范化名称,该属性包括C。如果这在 compile 方法中运行,则 Angular 应该像平常一样编译所有这些指令,添加任何附加功能/与其他指令(等)的接口,就像它对任何其他类型的元素所做的那样。 对吗?

这是我最直观地被吸引的选项 - 是否有任何我可能忽略的陷阱,这可能会使前一种策略更可取?

【问题讨论】:

  • 就 angular-d3 集成而言,这似乎工作得非常好:github.com/n3-charts/line-chart。与您的问题没有直接关系,但如果有人愿意,可以将其用作参考。
  • 我在 D3 和 Angular 方面的经验是,在数据绑定方面你应该只有一位大师。您可以在 Angular 中重新创建 D3 功能,但问题是您想在兔子洞中走多远。使用 ng-repeat 和简单的 SVG 可以轻松完成简单的条形图。但是力图需要 很多 更多的代码才能在 Angular 中复制。
  • 回复:restrict,是的,它在实践中似乎运行良好。在我们的例子中,我们使用attr 来附加我们想要的任何指令,允许我们从 d3 中“回到 Angular”。例如.attr('ng-tip', 'An {{ interpolated }} tip.')。然后我们只需$compile(svg)(其中svg 是实际节点)。
  • 在 Angular 2 的上下文中,这些事情是如何变化的?我们可以将 angular 和 D3 分开吗? D3 代码与 Angular 完全不同,我们是否应该将其封装在 Angular 组件中,让组件与应用程序的其余部分交互以获取数据和发送事件?

标签: javascript angularjs data-binding d3.js


【解决方案1】:

我一直在思考几乎相同的问题一段时间并得出以下结论。

将 Angular 创建的元素与 d3 集成的最简单方法是使用 .attr 添加指令,然后在 d3 生成的元素上添加 .call 编译服务。像这样:

mySvg.selectAll("circle")
                .data(scope.nodes)
                .enter()
                .append("circle")
                .attr("tooltip-append-to-body", true)
                .attr("tooltip", function(d){
                    return d.name;
                })
                .call(function(){
                    $compile(this[0].parentNode)(scope);
                });

Here is a Plunker.

我认为使用 Angular ngRepeat 而不是 d3 生成元素的想法与框架不符。 D3没想到会交到一堆元素。它希望获得数据 - 几乎总是一个数组。然后,它具有一堆出色的功能,可以将这些数据转换为各种 SVG 或 HTML 元素。让它这样做。

从这句话看来……

D3 使得添加元素并将每个元素绑定到它所代表的数据变得微不足道,但图形可视化只是一个更大的应用程序的一部分:我需要创建不同类型的元素来表示这些相同节点和边缘的不同部分应用程序(与 D3 无关),我想将所有这些元素绑定到单个数据集。

...您暗示使用 d3 生成元素会以某种方式阻止您将相同的数据绑定到应用程序的不同部分。我不明白为什么。只需让 d3 从范围数组中生成元素(就像在链接的 Plunker 中所做的那样)。然后以通常的 Angular 方式在任何你想要的地方使用相同的数据集。应用程序的其他部分可以更新数据集,$watch 回调可以重新渲染 d3 图形。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    • 2014-03-18
    • 2012-07-11
    • 2014-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多