D3.js JavaScript 模板中的图表(jsRender/jsViews、jquery tmpl 等)

D3.js Graphs in JavaScript Templates (jsRender/jsViews, jquery tmpl, etc.)

我一直在尝试创建一个页面,该页面使用 JavaScript 模板,其中包含使用 d3.js 制作的图表,以显示有关每个库存项目的信息(在本例中,我有一个数组 JSON 包含晶体管数据的 blob。)例如,我需要样板 "Absolute Max Ratings" table 后跟图形,例如一系列栅极电压的阻抗曲线。这是我目前所拥有的:

var partData = [
 {
  name: "IRF10",
  maxRatings:
  {
   vds: 200,
   vgs: 20,
   id: 2.1,
   pd: 36
  },
  impedanceGraph:
  [
   [1, 0.7],
   [2, 1.6],
   [3, 2.0],
   [4, 2.5]
  ]
 },
 {
  name: "2n7000",
  maxRatings:
  {
   vds: 60,
   vgs: 20,
   id: 0.2,
   pd: 0.4
  },
  impedanceGraph:
  [
   [1, 0.6],
   [2, 1.5],
   [3, 1.9],
   [4, 2.4]
  ]
 }
]

$('#container').html($('#partTemplate').render(partData));
<script id="partTemplate" type="text/x-jsrender">
    <div style="background-color:mintcream">
        <h2>{{>name}}</h2>
        Max Ratings
        {{props maxRatings}}
            <li>{{>key}}: {{>prop}}</li>
        {{/props}}
        <br />
        Impedance Graph: <br />
        PLACE_D3_GRAPH_HERE
    </div>
</script>
<div id="container"></div>

这是我的 jsfiddle,因为 jsRender 不能正常工作: http://jsfiddle.net/xaty70b6/

由于 D3.js 似乎通过将 SVG 元素附加到正文并将数据插入 selection 来工作,所以我被引导相信我需要以某种方式 select 使用 jquery 的模板实例并将其提供给 D3.js 以构建图形,但我不知道如何在构建行时获取模板实例。真的,我想要做的就是使用 JSON blob 中为该实例提供的数据,在我的模板实例中显示一个 D3.js 图表。是否有另一种方法可以使用相关数据数组插入要传递到 D3.js 的 SVG?

我没有使用过 D3.js,但在研究它时,很明显 D3 直接在 DOM 上工作 - 而 JsRender 渲染独立于 DOM -并生成一个 HTML 字符串,然后您自己将其插入 DOM。

然而 JsViews 完全是 'DOM aware' 并且在模板实例、数据和 DOM 之间进行绑定。所以我建议使用 JsViews。

此外,这开启了使用 JsViews 数据绑定以交互方式更新数据的可能性。 JsView 的数据绑定当然与 D3 的数据绑定不同,但是探索将它们集成在一起是很有趣的。

所以这里(在下面的代码片段中,还有这里:http://jsfiddle.net/BorisMoore/Lbfn5aog/1/)是一种方法。

我创建了一个自定义的 {{barChart}} 标签,用于集成 D3 图表:

$.views.tags("barChart", {
  init: function(tagCtx, linkCtx) {
    this.data = tagCtx.args[0];
    this.svg = initChart(linkCtx.elem, this.data);
  },
  onAfterLink: function(tagCtx, linkCtx) {
    var tag = this;
    $.observable(tag.data).observeAll(function() {
      updateChart(tag.data, tag.svg);
    });
  }
});

并将其包含在模板中,如下所示:

<div class="chart" data-link="{barChart impedanceGraph}"></div>

(也可以用标签呈现的 div 创建一个版本,这样你就可以写成 {^{barChart impedanceGraph /}} ...)

如果您不需要动态数据绑定,您可以省略 onAfterLink 方法。

但是如果你包含它,那么现在你可以修改文本框中的值,数据将由 JsViews 更新。数据链接的 barChart 标记将获取更改并将其传递给 D3,D3 将重新计算列,并根据需要使用新的比例因子。

var margin = {top: 20, right: 20, bottom: 30, left: 40},
  width = 180 - margin.left - margin.right,
  height = 200 - margin.top - margin.bottom;

function scaleChart(data) {
  var x = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1);

  var y = d3.scale.linear()
    .range([height, 0]);

  var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

  var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(10, "%");

  x.domain(data.map(function(d) { 
    return d.name; }));
  y.domain([0, d3.max(data, function(d) {
    return d.amt; 
  })]);

  return {x:x, y:y, xAxis: xAxis, yAxis: yAxis};
}

function initChart(element, data) {
  var chart = scaleChart(data);
  var svg = d3.select(element).append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(chart.xAxis);

  svg.selectAll(".bar")
    .data(data)
    .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) {
        return chart.x(d.name);
      })
      .attr("width", chart.x.rangeBand())
      .attr("y", function(d) { return chart.y(d.amt); })
      .attr("height", function(d) {
        return height - chart.y(d.amt);
      });
  return svg;
}

function updateChart(data, svg) {
  var chart = scaleChart(data);
    svg.selectAll(".bar")
      .data(data)
      .attr("y", function(d) { return chart.y(d.amt); })
      .attr("height", function(d) {
        return height - chart.y(d.amt);
      });
}

var partData = [
  {
    name: "IRF10",
    maxRatings:
    {
      vds: 200,
      vgs: 20,
      id: 2.1,
      pd: 36
    },
    impedanceGraph:
    [
      {name: 1, amt: 0.7},
      {name: 2, amt: 1.6},
      {name: 3, amt: 2.0},
      {name: 4, amt: 2.0},
      {name: 5, amt: 0.5}
    ]
  },
  {
    name: "2n7000",
    maxRatings:
    {
      vds: 60,
      vgs: 20,
      id: 0.2,
      pd: 0.4
    },
    impedanceGraph:
    [
      {name: 1, amt: 0.6},
      {name: 2, amt: 1.5},
      {name: 3, amt: 1.9},
      {name: 4, amt: 2.4}
    ]
  }
]

$.views.tags("barChart", {
  init: function(tagCtx, linkCtx) {
    this.data = tagCtx.args[0];
    this.svg = initChart(linkCtx.elem, this.data);
  },
  onAfterLink: function(tagCtx, linkCtx) {
    var tag = this;
    $.observable(tag.data).observeAll(function() {
      updateChart(tag.data, tag.svg);
    });
  }
});

var tmpl = $.templates("#partTemplate");

tmpl.link('#container', partData);
li {
  list-style:none
}
.bar {
  fill: steelblue;
}

.bar:hover {
  fill: brown;
}

.axis {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//www.jsviews.com/download/jsviews.js"></script>

<script id="partTemplate" type="text/x-jsrender">
    <div style="background-color:mintcream">
        <h2>{{>name}}</h2>
        Max Ratings
        <ul>
          {{props maxRatings}}
            <li>{{>key}}: {{>prop}}</li>
          {{/props}}
        </ul>
        <br />
        Impedance Graph: <br />
        <ul>
          {{for impedanceGraph}}
            <li><input data-link="amt" /></li>
          {{/for}}
        </ul>
        <div class="chart" data-link="{barChart impedanceGraph}"></div>
    </div>
</script>

<div id="container"></div>