当Ember负责数据连接时,d3在Ember中转换

时间:2014-12-15 21:33:02

标签: ember.js d3.js

我喜欢在我的Handlebars文件中定义Ember中的d3图表,并尽可能避免d3.select()和数据连接。我不想使用d3的数据连接,而是依赖于Ember的计算属性。实际上,在我在Ember中的d3可视化中,我倾向于将我的数据点包装在{{each}}循环中,并且当底层数据发生变化时,Ember会负责更新图表。这一切都很好,但有一个重要的d3功能,我觉得我正在失去这种方式,即transitions。我喜欢在底层数据的两个状态之间进行转换。是否有任何方法同时依赖Ember进行数据连接(并避免d3.select()。data()舞蹈)并且仍能以某种方式应用d3过渡?

这是一个示例代码,我想在更改高度时将条形图从一个状态转换为另一个状态(这是一个简单的例子,但我认为这是说明性的)。在此先感谢您的帮助!

// bar-chart.js

export default Ember.Component.extend({
    tagName: "svg",
    attributeBindings: ["width", "height"],
    width: 950,
    height: 600,
    padding: { top:..., bottom:..., left:..., right:... },
    transformG: function() {
        var padding = this.get("padding");
        return "translate(%@,%@)".fmt(padding.left, padding.top);
    }.property(),
    chartW: function() {
        ...
    }.property("width"),
    chartH: function() {
        ...
    }.property("height"),
    yScale: function() {
        var data = this.get("data");
        return d3.scale.linear().domain(d3.extent(data, function(d) { return d.value; })).range([...]);
    }.property("data", "chartH"),
    xScale: function() {
        ...
    }.property("data", "width"),
    actions: {
        // Some action that modifies the height of the svg
    }
});

// bar-chart.hbs

<g class="axis x"></g>
<g class="axis y"></g>
<g class="bars" {{bind-attr transform=transformG width=chartW height=chartH}}>
    {{#each d in data}}
        {{vertical-bar value=d.value timestamp=d.timestamp yScale=yScale xScale=xScale}}
    {{/each}}
<g>

假设子组件{{vertical-bar}}负责计算与每个单独栏相关的一些属性。

1 个答案:

答案 0 :(得分:0)

尽管d3进入/更新/退出范例可能令人困惑/繁琐,但它确实处于d3的核心并尝试覆盖它但仍然使用d3的其他部分(如transition)将可能会给你带来很多麻烦。

例如:

d3一次绑定svg元素,但我认为你在这里做的事情会在每次data更改时重新呈现元素,而这可能不是你想要的。如果您尝试使用transition并同时更新了Ember中的数据,那么事情可能会爆炸。


我熟悉&#34; d3.select().data()舞蹈的头​​痛&#34;但最终通常意味着我并没有很好地理解我的数据的形状。是否有一些技术原因你不想这样做?或者each d in data在概念上更容易?

另类

你可能想要考虑一个由d3消费的data的中间集,但是由Ember控制。我当前使用的策略是创建一个ObjectProxy的数组来包装一些自定义属性,然后将整个事件传递给d3:

ObjectProxy.create({content: {timestamp: d.x, some_property: d.foo, type: "bar" }})

当您更新此中间集时,您可以简单地将整个内容再次传递给d3,并且在正确设置输入/更新/退出选择的情况下它将执行正确的操作。

确保其有效的一个提示是在您的基准上设置唯一ID并在选择期间使用它:

svg.selectAll("rect").data(@get("myData"), (d) -> d.get("id"))

注意:coffeescript =)

Ember w / D3

我认为允许Ember控制输入的数据大概是在你使用D3完成其余工作之前就已经去了,否则你只是混淆了责任而你也可以推出你自己的图库。上面的替代方案为您提供了更多的控制,并为您的基准添加了计算属性,可以这样使用:

selection.transition().attr({x: (d) -> d.get("end_x")})其中end_x是Ember为您处理的事情。

或:.on("click", (d,i) => d.get("controller").transitionToRoute("myRoute", d.id))