Created
April 21, 2013 00:49
-
-
Save tomsdev/5428018 to your computer and use it in GitHub Desktop.
MeteorJS sample that refresh only the parts of a d3js graph that have changed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.bar {fill: #1D8300;} | |
.label {fill: #FFF; font-size:10px; font-family:sans-serif;} | |
fieldset {margin-bottom:20px} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<head> | |
<title>testd3js</title> | |
</head> | |
<body> | |
{{> editor}} | |
{{> chart}} | |
</body> | |
<template name="editor"> | |
<div class="editor"> | |
<fieldset> | |
<input type="text" name="width" placeholder="width"> | |
<input type="text" name="height" placeholder="height"> | |
<input type="text" name="data" placeholder="data e.g. 10,40,20"> | |
</fieldset> | |
</div> | |
</template> | |
<template name="chart"> | |
<div class="chart"> | |
{{#constant}} | |
<svg width="500" height="500"> | |
</svg> | |
{{/constant}} | |
</div> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if (Meteor.isClient) { | |
// HACK : A custom reactive data source that works similarly | |
// to the Session object but can store Function type ! | |
Session = { | |
keys: {}, | |
deps: {}, | |
get: function (key) { | |
this.ensureDeps(key); | |
this.deps[key].depend(); | |
return this.keys[key]; | |
}, | |
set: function (key, value) { | |
this.keys[key] = value; | |
this.ensureDeps(key); | |
this.deps[key].changed(); | |
}, | |
ensureDeps: function (key) { | |
if (!this.deps[key]) | |
this.deps[key] = new Deps.Dependency(); | |
} | |
}; | |
Meteor.startup(function () { | |
// default reactive values of the chart | |
Session.set("width", 200); | |
Session.set("height", 200); | |
Session.set("data", [{name:"Foo", value:10}, {name:"Bar", value:20}, {name:"Baz", value:60}]); | |
}); | |
// class that construct a bar chart | |
function BarChart(node) { | |
console.log("BarChart"); | |
var svg = d3.select(node); | |
this.barContainer = svg.append("g"); | |
this.labelContainer = svg.append("g"); | |
// autorun on methods that will refresh different part of the graph | |
this.autoruns = []; | |
this.autoruns.push(Deps.autorun(this.updateScaleY.bind(this))); | |
this.autoruns.push(Deps.autorun(this.updateScaleX.bind(this))); | |
this.autoruns.push(Deps.autorun(this.updateBars.bind(this))); | |
this.autoruns.push(Deps.autorun(this.updateLabels.bind(this))); | |
} | |
// destroy each autorun | |
BarChart.prototype.destroy = function () { | |
console.log("destroy"); | |
var autorun; | |
while (autorun = this.autoruns.pop()) { | |
autorun.stop(); | |
} | |
}; | |
BarChart.prototype.updateScaleY = function () { | |
console.log("updateScaleY"); | |
// get reactive dependencies | |
var height = Session.get("height"); | |
var data = Session.get("data"); | |
// construct a new d3js scale function | |
var val = d3.scale.linear() | |
.domain([0, d3.max(data, function(d) { return d.value; })]) | |
.range([height, 0]); | |
// store it as a reactive function | |
Session.set("scaleY", val); | |
}; | |
// construct a new d3js scale function | |
// and store it as a reactive function | |
BarChart.prototype.updateScaleX = function () { | |
console.log("updateScaleX"); | |
// get reactive dependencies | |
var width = Session.get("width"); | |
var data = Session.get("data"); | |
// construct a new d3js scale function | |
var val = d3.scale.ordinal() | |
.domain(d3.range(0, data.length)) | |
.rangeRoundBands([0, width], .1); | |
// store it as a reactive function | |
Session.set("scaleX", val); | |
}; | |
BarChart.prototype.updateBars = function () { | |
console.log("updateBars"); | |
// get reactive dependencies | |
var data = Session.get("data"); | |
var height = Session.get("height"); | |
// get reactive function dependencies | |
var scaleX = Session.get("scaleX"); | |
var scaleY = Session.get("scaleY"); | |
// d3js code to create/update/remove the bars | |
var bars = this.barContainer.selectAll(".bar").data(data); | |
bars.enter().append("rect") | |
.attr("class", "bar"); | |
bars | |
.attr("x", function(d, i) { return scaleX(i); }) | |
.attr("width", scaleX.rangeBand()) | |
.attr("y", function(d) { return scaleY(d.value); }) | |
.attr("height", function(d) { return height - scaleY(d.value); }); | |
bars.exit().remove(); | |
}; | |
BarChart.prototype.updateLabels = function () { | |
console.log("updateLabels"); | |
// get reactive dependencies | |
var data = Session.get("data"); | |
// get reactive function dependencies | |
var scaleX = Session.get("scaleX"); | |
var scaleY = Session.get("scaleY"); | |
// d3js code to create/update/remove the labels | |
var labels = this.labelContainer.selectAll(".label").data(data); | |
labels.enter().append("text") | |
.attr("class", "label"); | |
labels.attr("x", function(d, i) { return scaleX(i) + scaleX.rangeBand() / 2 - 8; }) | |
.text(function (d,i) { return d.name; }) | |
.attr("y", function(d) { return scaleY(d.value) + 10; }); | |
labels.exit().remove(); | |
}; | |
// form events that update reactive values of the chart | |
Template.editor.events({ | |
'change input[name=width]' : function (event) { | |
var val = +event.target.value; | |
Session.set("width", val); | |
}, | |
'change input[name=height]' : function (event) { | |
var val = +event.target.value; | |
Session.set("height", val); | |
}, | |
'change input[name=data]' : function (event) { | |
var val = event.target.value; | |
var data = val | |
.split(',') | |
.map(function(d, i){ | |
return { | |
name: "#"+(i+1), | |
value: parseInt(d) | |
}; | |
}); | |
Session.set("data", data); | |
} | |
}); | |
Template.chart.rendered = function () { | |
console.log("rendered"); | |
this.node = this.find("svg"); | |
if (! this.instance) { | |
// create the bar chart once | |
this.instance = new BarChart(this.node); | |
} | |
}; | |
Template.chart.destroyed = function () { | |
this.instance && this.instance.destroy(); | |
}; | |
} | |
if (Meteor.isServer) { | |
Meteor.startup(function () { | |
// code to run on server at startup | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment