This is a six part series, taking you through stages of designing and creating reusable visualizations with d3.js
All visualizations have the same functionality, showcase the individual points with a bar chart and sum up the selected bars.
Part 4: This is showcasing the power of combining ember.js with d3.js and how to bind them together creating an abstraction between the visualization (the bar chart) and the rest of the application.
These are examples created for a talk (slides and video).
Cheers,
Miles @milr0c
xxxxxxxxxx
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="https://littlesparkvt.com/flatstrap/assets/css/bootstrap.css"/>
<link rel="stylesheet" type="text/css" href="style.css"/>
<script src="https://code.jquery.com/jquery-2.0.0.min.js"></script>
<script src="handlebars.min.js"></script>
<script src="ember.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="src.js"></script>
</head>
<body>
<script type="text/x-handlebars" data-template-name="application">
<div class="row">
<div class="span2">
<button class="btn btn-success" {{action update target="view" }}>update</button>
</div>
<div class="span2" id="sum">
TOTAL: {{view.total}}
</div>
</div>
<div class="row">
{{view App.barView contentBinding="view.barValues"}}
</div>
</script>
<script type="text/javascript">
App = Ember.Application.create({});
// Controllers
App.mainController = Ember.ArrayProxy.create({
content: [],
total: 0,
init: function() {
this.update();
},
randomize: function(n, y) {
if (arguments.length < 2) y = 400;
if (!arguments.length) n = 20;
var i = 0;
this.set('content', d3.range(~~(Math.random()*n) + 1).map(function(d, i) { return { x: ++i, y: ~~(Math.random()*y) };
}));
},
update: function() {
this.randomize();
this.set('total', 0);
},
sum: function(data, extent, x) {
if (!arguments.length) return 0;
var total = 0;
data.forEach(function(d) {
if (extent[0] <= x(d.x) && x(d.x) + x.rangeBand() <= extent[1]) {
total += d.y;
}
});
// weird namespacing issue, need to learn more
this.App.mainController.set('total', total);
}
});
// Views
App.ApplicationView = Ember.View.extend({
templateName: 'application',
barValuesBinding: 'App.mainController.content',
totalBinding: 'App.mainController.total',
update: function(event) {
App.mainController.update();
}
});
App.barView = Ember.View.extend({
sumBinding: 'App.mainController.sum',
change: function() {
var elementId = this.get('elementId'),
data = this.get('content'),
sum = this.get('sum'),
bar = this.get('bar') || charts.bar();
bar.on('brush', function(data) {
var extent = d3.event.target.extent(),
x = bar.x();
sum(data, extent, x);
});
bar.on('brushend', function(data) {
var extent = d3.event.target.extent(),
x = bar.x();
sum(data, extent, x);
});
d3.select('#'+elementId)
.datum(data)
.call(bar);
this.set('bar', bar);
}.observes('content'),
didInsertElement: function() {
this.change();
}
});
// Routes
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/'
})
})
});
</script>
</body>
</html>
Modified http://code.jquery.com/jquery-2.0.0.min.js to a secure url
Modified http://d3js.org/d3.v3.min.js to a secure url
https://code.jquery.com/jquery-2.0.0.min.js
https://d3js.org/d3.v3.min.js