<html>
<head>
<meta name="description" content="Dynamic Charts with Ember.js and D3" />
<meta charset=utf-8 />
<title>Dynamic Charts with Ember.js and D3</title>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://builds.emberjs.com/handlebars-1.0.0-rc.4.js"></script>
<script src="http://builds.emberjs.com/ember-1.0.0-rc.5.js"></script>
<script src="http://builds.emberjs.com/ember-data-latest.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.1.6/d3.min.js"></script>
</head>
<body>
<script type="text/x-handlebars" data-template-name="application">
<h3>Dynamic Charts with Ember.js and D3</h3>
<a href="#" {{action generateNewChartValues target="controller"}} class="btn btn-info">Generate new Values</a>
{{view App.ChartView contentBinding="model"}}
</script>
</body>
</html>
path.line {
fill: none;
stroke: red;
stroke-width: 1.5px;
opacity: .5;
}
path.area {
fill: red;
opacity: .2;
}
.axis {
shape-rendering: crispEdges;
font-size: 8pt;
}
.axis path {
fill: none;
stroke: #000;
}
.axis line {
stroke: #eee;
}
body {
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
margin: 5px;
}
h1 {
font-weight: bold;
font-size: 16pt;
margin-bottom: 5px;
}
.button {
color: #555;
background-color: #EEE;
padding: 3px 8px;
border: 1px solid #CCC;
text-decoration: none;
font-size: 10pt;
border-radius: 6px;
box-shadow: 0 2px 2px #DDD;
}
.button:hover {
color: #111;
border-color: #888;
}
.button:active {
text-shadow: 0 1px 2px #CCC;
box-shadow: 0 1px 2px #DDD;
}
App = Ember.Application.create({
ready: function() {
console.log('App ready');
}
});
App.ApplicationController = Ember.ArrayController.extend({
needs: ['chartValues'],
chartValuesBinding: 'controllers.chartValues.content',
init: function() {
this.generateNewChartValues();
},
generateNewChartValues: function() {
this.get('controllers.chartValues').send('replaceWithRandom');
}
});
App.ApplicationRoute = Ember.Route.extend({
model: function() {
var controller = this.controllerFor('chartValues');
controller.send('replaceWithRandom');
//return controller.get('content');
return App.ChartValue.find();
}
});
App.ChartView = Ember.View.extend({
chart: {},
line: {},
area: {},
updateChart: function () {
var content = this.get('content');
var chart = this.get('chart');
var line = this.get('line');
var area = this.get('area');
chart.selectAll('path.line')
.data(content)
.transition()
.duration(500)
.ease('sin')
.attr('d', line(content));
chart.selectAll('path.area')
.data(content)
.transition()
.duration(500)
.ease('sin')
.attr('d', area(content));
}.observes('content.@each.value'),
didInsertElement: function () {
var elementId = this.get('elementId');
var content = this.get('content');
var acontent = content.toArray();
console.log(acontent[0].get('timestamp'));
console.log(acontent[1].get('timestamp'));
console.log(acontent[0].get('value'));
console.log(acontent[1].get('value'));
console.log(elementId);
var margin = { top: 35, right: 35, bottom: 35, left: 35};
var w = 500 - margin.right - margin.left;
var h = 300 - margin.top - margin.top;
var x = d3.scale.linear()
.range([0,w])
.domain([1,acontent.length]);
var y = d3.scale.linear()
.range([h,0])
.domain([0,130]);
var xAxis = d3.svg.axis()
.scale(x)
.ticks(10)
.tickSize(-h)
.tickSubdivide(true);
var yAxis = d3.svg.axis()
.scale(y)
.ticks(4)
.tickSize(-w)
.orient('left');
// Prepeare Chart Elements:
var line = d3.svg.line()
.interpolate('monotone')
.x(function(d) { //console.log(d.get("timestamp"));
return x(d.get('timestamp'));})
.y(function(d) { return y(d.get('value'));});
this.set('line',line);
var area = d3.svg.area()
.interpolate('monotone')
.x(function(d) { return x(d.get('timestamp')); })
.y0(h)
.y1(function(d) { return y(d.get('value')); });
this.set('area',area);
var chart = d3.select('#'+elementId).append('svg:svg')
.attr('id','chart')
.attr('width', w+margin.left+margin.right)
.attr('height', w+margin.top+margin.bottom)
.append('svg:g')
.attr('transform', 'translate('+margin.left+','+margin.top+')');
// Add Chart Elements to Chart:
chart.append('svg:g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + h + ')')
.call(xAxis);
chart.append('svg:g')
.attr('class', 'y axis')
.call(yAxis);
chart.append('svg:clipPath')
.attr('id', 'clip')
.append('svg:rect')
.attr('width', w)
.attr('height', h);
chart.append('svg:path')
.attr('class', 'area')
.attr('clip-path', 'url(#clip)')
.attr('d', area(acontent));
chart.append('svg:path')
.attr('class', 'line')
.attr('clip-path', 'url(#clip)')
.attr('d', line(acontent));
this.set('chart', chart);
}
});
App.ChartValuesController = Ember.ArrayController.extend({
content: [],
replaceWithRandom: function () {
var newContent = Ember.A([]);
var max = 100;
for(var i = 0, l = 100; i < l; i++) {
var item = App.ChartValue.createRecord({
timestamp: i,
value: max/2+Math.sin(i)*Math.ceil((max/2.5)*Math.random())
});
newContent.pushObject(item);
}
this.set('content', newContent);
}
});
App.ChartValue = DS.Model.extend({
timestamp: DS.attr('number'),
value: DS.attr('number')
});
App.Store = DS.Store.extend({
revision: 13,
adapter: 'DS.FixtureAdapter'
});
App.ChartValue.FIXTURES = [
{
id: 1,
timestamp: 1,
value: 12.04
},
{
id: 2,
timestamp: 2,
value: 18.04
},
{
id: 3,
timestamp: 3,
value: 100.00
},
{
id: 4,
timestamp: 4,
value: 12.04
},
{
id: 5,
timestamp: 5,
value: 50.04
},
{
id: 6,
timestamp: 6,
value: 112.04
},
{
id: 1,
timestamp: 7,
value: 12.04
},
];
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |