【问题标题】:Angular + d3.js: Data binding with SVG text attr?Angular + d3.js:与 SVG 文本属性的数据绑定?
【发布时间】:2014-03-18 14:18:25
【问题描述】:

我正在处理一个涉及更新出现在圆圈内的文本元素的可视化。比如:

<g>
    <circle></circle>
    <text>My text that needs updating</text>
</g>

除了这个小视觉效果外,还有一个 d3 画笔用作滑块。在刷子/刷子上,我需要&lt;text&gt;Update the text here&lt;/text&gt;,基于与滑块 x 轴上的时间刻度相关的数据。

我有两个 Angular 指令。一个包含圆圈,另一个是滑块。在我调用的滑块指令中:

$rootScope.$emit('brushing'); 并根据我的数据设置一个 rootScope 变量,$rootScope.myNewValue = newValue; 发生滑动

然后我在我的另一个指令中监听这个,并将 text.attr 更新为 rootScope var:

$rootScope.$on('brushing', function () { myText.text($rootScope.myNewValue); });

地点:

var myText = svg.append('text')
    .text('I'm trying to update this text..');

...在代码的 d3 部分

这样做好像行得通,不过我想知道有没有办法在初始化myText的时候直接绑定数据:

var myText = svg.append('text)
    .text($rootScope.myNewValue);

这样值就可以直接更新而无需$emit和listen。我在指令中尝试了scope.$apply,但这似乎没有效果。

有没有人在使用 d3 和 Angular 时遇到过类似的情况?

【问题讨论】:

  • 您是否尝试过仅使用角度绑定将文本绑定到变量?这是一篇关于直接将 SVG 与 Angular 结合使用的好文章:alexandros.resin.io/angular-d3-svg
  • 感谢您的资源。我会看看这个。

标签: javascript angularjs d3.js


【解决方案1】:

您可以将 D3JS 代码包装在指令中,然后使用传入的属性并观察它是否会更改以相应地更新 D3JS 代码(保存对 svg 文本的引用可能仍然是最好的选择)。有关示例,请参见下面的指令,其中我使用 val 更新条形图显示中使用的数据(注意我还没有在这里处理转换,代码有点混乱,但希望你能看到一般观点)

directive('barChart', function ( /* dependencies */ ) {
  // define constants and helpers used for the directive

  var width = 600,
    height = 80;

  return {
    restrict: 'E', // the directive can be invoked only by using <bar-chart></bar-chart> tag in the template
    scope: { // attributes bound to the scope of the directive
      val: '='
    },
    link: function (scope, element, attrs) {
      // initialization, done once per my-directive tag in template. If my-directive is within an
      // ng-repeat-ed template then it will be called every time ngRepeat creates a new copy of the template.

      // set up initial svg object
      var vis = d3.select(element[0])
        .append("svg")
          .attr("class", "chart")
          .attr("width", width)
          .attr("height", height);


      // whenever the bound 'exp' expression changes, execute this 
      scope.$watch('val', function (newVal, oldVal) {

        // clear the elements inside of the directive
        vis.selectAll('*').remove();
        vis.attr("height", newVal.length*22);

        // if 'val' is undefined, exit
        if (!newVal) {
          return;
        }
        var totalDataSetSize = 0;

        for (var i = 0; i < newVal.length; i++) {
          totalDataSetSize += newVal[i].data.length
        };

        function calcBarWidth(d) {
          return (totalDataSetSize==0)?0:d.data.length/totalDataSetSize*420;
        }

        vis.selectAll("rect")
            .data(newVal)
          .enter().append("rect")
            .attr("y", function(d, i) { return i*20; })
            .attr("width", calcBarWidth)
            .attr("height", function(d) {return 20});

        vis.selectAll("text")
            .data(newVal)
          .enter().append("text")
            .attr("x", function(d) { return calcBarWidth(d)+10})
            .attr("y", function(d, i) { return (i+1)*20; })
            .attr("dy", "-.3em") // vertical-align: middle
            .style("font-size", ".7em")
            .attr("fill", "black")
            .attr("text-anchor", "beginning") // text-align: right
            .text(function(d,i){ return d.data.length.toString() + "  " + d.label})

      },true);
    }
  };
})

【讨论】:

  • 谢谢。这可能会奏效,我会试一试。我没想过要设置手表。我目前通过 attr 而不是隔离范围的属性传递值,但我预计会得到类似的结果。
  • 是的,我认为这对你有用,如果有问题请告诉我,因为我打算继续使用它:)
  • 会的。还没有机会尝试,但今天下午会回到代码中并发布更新。
  • 我们稍微改变了应用程序的方法。我们现在正在做的是操纵我们的滑块,根据定位发送一个 XHR 请求,然后更新我们的 UI。但是,此解决方案适用于新案例和我们之前的方法。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-01
  • 1970-01-01
  • 2016-08-18
  • 2019-04-12
  • 2017-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多