I tried with d3. In fact d3 is pretty low level too. You have to build your chart in code. There are no predefined charts to use. You have only tools to build charts. So with KO you should build viewmodel for chart and bind it to d3 chart and yes it will rebuild node tree on each update. Another approach (I ended up with it) is to build charts entirely with KO I only look in d3 samples and code for ideas. I use separate viewmodel and template for each chart component. For example for tree diagram i have 3 view models:
1. Chart node viewmodel which at minimum should have node coordinates as ko.observable.
2. Node link viewmodel containing references to nodes it links to as ko.observables and start/end coordinates as ko.observables. Based on start/end coordinates I build link path (ko.computed).
3. Chart viewmodel with 2 observable arrayas - Nodes and Links.
So chart template looks like:
<script id='stateChartTemplate' type='text/html'>
<g class="container" transform="translate(0,0)" data-bind="autoStretch:{ width: width, height: height, updateElement: false}">
<!-- ko foreach: links -->
<path class="link" data-bind="attr: { d:path }" />
<!-- /ko -->
<!-- ko foreach: nodes -->
<!-- ko template: {name: 'svgQueue', templateEngine: SVGTemplateEngine} -->
<!-- /ko -->
<!-- /ko -->
</g>
</script>
<script id='svgQueue' type='text/html'>
<g class="node" data-bind="attr: { transform: 'translate('+rx()+','+ry()+')' }">
<rect class="node-rect" width="150" height="50" data-bind="sizeFromAttr: { width: width, height: height}" />
<text text-anchor="start" dx="3" dy="15" data-bind="text: name"></text>
</g>
</script>
sizeFromAttr binding just gets width and height from attributes and places it into viewmodel.
So in fact there is no need in d3 itself with that approach.
SVGTemplateEngine is a hack for KO native template engine to properly render svg (I have googled it):
var SVGTemplateEngine = function () {
this['allowTemplateRewriting'] = false;
}
SVGTemplateEngine.prototype = new ko.templateEngine();
SVGTemplateEngine.prototype.renderTemplateSource = function (templateSource, bindingContext, options) {
var nodes = templateSource.nodes();
if (nodes)
return nodes;
var div = document.createElement('div');
var text = '<svg xmlns="
http://www.w3.org/2000/svg">' + templateSource.text() + '</svg>';
div.innerHTML = text;
return ko.utils.arrayPushAll([], div.childNodes[0].childNodes);
};
window.SVGTemplateEngine = new SVGTemplateEngine();