var app = angular.module('plunker', ['nvd3']);
app.controller('MainCtrl', function($scope, nvd3XRangeBarsPlugin, nvd3XMarkersPlugin, nvd3Plugins) {
nvd3Plugins.addPlugin('x-range-bars', nvd3XRangeBarsPlugin);
nvd3Plugins.addPlugin('x-markers', nvd3XMarkersPlugin);
var dataBars = [{
label: 'ver 1',
from: -10,
to: 15
}, {
label: 'ver 2.0',
from: 25,
to: 40
}];
var dataMarkers = [{
label: 'beta',
x: 30,
}, {
label: 'release',
x: 42
}];
$scope.options = {
plugins: [{
type: 'x-range-bars'
}, {
type: 'x-markers'
}, ],
chart: {
zoom: true,
type: 'lineChart',
height: 450,
margin: {
top: 20,
right: 20,
bottom: 40,
left: 55
},
x: function(d) {
return d.x;
},
y: function(d) {
return d.y;
},
useInteractiveGuideline: true,
dispatch: {
stateChange: function(e) {
console.log("stateChange");
},
changeState: function(e) {
console.log("changeState");
},
tooltipShow: function(e) {
console.log("tooltipShow");
},
tooltipHide: function(e) {
console.log("tooltipHide");
}
},
xAxis: {
axisLabel: 'Time (ms)',
tickFormat:function (d) {
return d3.time.format('%X')(new Date(d*3600))
}
},
yAxis: {
axisLabel: 'Voltage (v)',
tickFormat: function(d) {
return d3.format('.02f')(d);
},
axisLabelDistance: -10
},
callback: function(chart) {
console.log("!!! lineChart callback !!!");
}
},
title: {
enable: true,
text: 'Title for Line Chart'
},
subtitle: {
enable: true,
text: 'Subtitle for simple line chart. Lorem ipsum dolor sit amet, at eam blandit sadipscing, vim adhuc sanctus disputando ex, cu usu affert alienum urbanitas.',
css: {
'text-align': 'center',
'margin': '10px 13px 0px 7px'
}
},
caption: {
enable: true,
html: '<b>Figure 1.</b> Lorem ipsum dolor sit amet, at eam blandit sadipscing, <span style="text-decoration: underline;">vim adhuc sanctus disputando ex</span>, cu usu affert alienum urbanitas. <i>Cum in purto erat, mea ne nominavi persecuti reformidans.</i> Docendi blandit abhorreant ea has, minim tantas alterum pro eu. <span style="color: darkred;">Exerci graeci ad vix, elit tacimates ea duo</span>. Id mel eruditi fuisset. Stet vidit patrioque in pro, eum ex veri verterem abhorreant, id unum oportere intellegam nec<sup>[1, <a href="https://github.com/krispo/angular-nvd3" target="_blank">2</a>, 3]</sup>.',
css: {
'text-align': 'justify',
'margin': '10px 13px 0px 7px'
}
}
};
$scope.data = sinAndCos();
$scope.data.push({
plugin: 'x-range-bars',
values: dataBars
});
$scope.data.push({
plugin: 'x-markers',
values: dataMarkers
});
/*Random Data Generator */
function sinAndCos() {
var sin = [],
sin2 = [],
cos = [];
//Data is represented as an array of {x,y} pairs.
for (var i = 0; i < 100; i++) {
sin.push({
x: i,
y: Math.sin(i / 10)
});
sin2.push({
x: i,
y: i % 10 == 5 ? null : Math.sin(i / 10) * 0.25 + 0.5
});
cos.push({
x: i,
y: .5 * Math.cos(i / 10 + 2) + Math.random() / 10
});
}
//Line chart data should be sent as an array of series objects.
return [{
values: sin, //values - represents the array of {x,y} data points
key: 'Sine Wave', //key - the name of the series.
color: '#ff7f0e', //color - optional: choose your own line color.
strokeWidth: 2,
classed: 'dashed'
}, {
values: cos,
key: 'Cosine Wave',
color: '#2ca02c'
}, {
values: sin2,
key: 'Another sine wave',
color: '#7777ff',
area: true //area - set to true if you want this line to turn into a filled area chart.
}];
}
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>Angular-nvD3 Line Chart</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.4/angular-material.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.4/angular-material.layouts.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.min.css" />
<link rel="stylesheet" href="style.css" />
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.4/angular-material.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-nvd3/1.0.5/angular-nvd3.js"></script>
<script src="angular-nvd3x.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<nvd3x options="options" data="data"></nvd3x>
<br><a href="http://krispo.github.io/angular-nvd3/" target="_blank" style="float: right;">See more</a>
</body>
</html>
/* Put your css in here */
.dashed {
stroke-dasharray: 5, 5;
}
.u-bar:nth-child(odd) rect {
fill: rgba(0, 170, 255, 0.08);
}
.u-bar:nth-child(even) rect {
fill: rgba(0, 0, 0, 0.08);
}
.u-bar text {
fill: rgba(0, 0, 0, 0.3);
}
.u-marker line {
stroke: navy;
stroke-dasharray: 5, 5;
}
.u-marker rect {
stroke: black;
/* stroke-dasharray: 5, 5; */
fill: rgba(255, 255, 255, 0.5);
}
.u-marker text {
/* font-size: 30px !important; */
}
Read more about Angular-nvD3:
http://krispo.github.io/angular-nvd3/
(function(window) {
'use strict';
var nv = window.nv;
// Node.js or CommonJS
if (typeof(exports) !== 'undefined') {
/* jshint -W020 */
nv = require('nvd3');
/* jshint +W020 */
}
angular.module('nvd3')
.directive('nvd3x', ['nvd3Utils', 'nvd3Plugins', function(nvd3Utils, nvd3Plugins) {
return {
restrict: 'AE',
scope: {
data: '=', //chart data, [required]
options: '=', //chart options, according to nvd3 core api, [required]
api: '=?', //directive global api, [optional]
events: '=?', //global events that directive would subscribe to, [optional]
config: '=?', //global directive configuration, [optional]
onReady: '&?' //callback function that is called with internal scope when directive is created [optional]
},
template: '<nvd3 options="options" data="chartData" api="api" events="events" config="config" on-ready="onReadyHandler"></nvd3>',
link: function($scope, element, attrs) {
$scope.chartData = extractChartData($scope.data);
function triggerReady(scope, el) {
var onReady = $scope.onReady({scope:scope, el:el});
if (angular.isFunction(onReady)) {
onReady(scope, el);
}
}
function findPluginOptions(type) {
for (var i = 0; i < $scope.options.plugins.length; i++) {
var options = $scope.options.plugins[i];
if (options.type === type) {
return options;
}
}
}
function findPluginData(type) {
if (!type) {
console.log('Type should be specified');
return;
}
for (var i = 0; i < $scope.data.length; i++) {
var data = $scope.data[i];
if (data.plugin === type) {
return data;
}
}
}
function renderPlugins(chart) {
var originalUpdate = chart.update;
chart.update = function() {
originalUpdate.apply(this, [].slice.call(arguments));
var container = d3.select(chart.container);
container.transition().call(function() {
renderPlugins(chart);
});
};
$scope.plugins.forEach(function(plugin) {
plugin.render(chart, plugin.options, findPluginData(plugin.options.type));
});
}
function extractChartData(data) {
return data.filter(function(entry) {
return !entry.plugin;
});
}
function updateApi() {
//override functions to treat data and restore connection to plugins update
var updateWithData = $scope.api.updateWithData;
$scope.api.updateWithData = function(data) {
if (data) {
updateWithData(extractChartData(data));
$scope.data = data;
} else {
updateWithData();
}
renderPlugins($scope.api.getScope().chart);
};
var update = $scope.api.update;
$scope.api.update = function() {
update();
renderPlugins($scope.api.getScope().chart);
};
}
$scope.onReadyHandler = function(scope, el) {
updateApi();
if ($scope.options.plugins) {
$scope.plugins = $scope.options.plugins.map(function(options) {
return {
render: nvd3Plugins.getPluginInstance(options.type),
options: options
};
});
if ($scope.plugins && $scope.plugins.length) {
renderPlugins(scope.chart);
}
}
triggerReady(scope, el);
};
$scope.$watchCollection('data', function(newData) {
$scope.chartData = extractChartData(newData);
});
}
};
}])
.service('nvd3Plugins', function() {
var plugins = {};
this.addPlugin = function(name, plugin) {
if (plugins[name]) {
console.warn('NVD3x plugin "', name, '" already exists. Replacing with new one');
}
plugins[name] = plugin;
};
this.getPluginInstance = function(name) {
if (!plugins[name]) {
console.warn('NVD3x plugin "', name, '" doesn\'t exist.');
return angular.noop;
}
return plugins[name]();
};
})
.factory('nvd3XRangeBarsPlugin', function() {
return function nvd3XRangeBarsPlugin() {
var id = Math.floor(Math.random() * 10000);
return function nvd3XRangeBarsRender(chart, options, data) {
var xAxis = chart.xAxis || chart.xAxis1 || chart.x1Axis;
var yAxis = chart.yAxis || chart.yAxis1 || chart.y1Axis;
var axis = d3.select(chart.container).selectAll('g.nv-axis');
var container = d3.select(axis[0][0].parentNode);
var clipEdge = true;
var xScale = xAxis.scale();
var yScale = yAxis.scale();
var yRange = yAxis.range();
var xRange = xAxis.range();
var availableWidth = xRange[1];
var availableHeight = yRange[0];
var wrap = container.selectAll('g.u-wrap.u-x-ranges').data([data]);
var wrapEnter = wrap.enter().insert('g', '.nv-axis + g:not(.nv-axis)').attr('class', 'nvd3 u-wrap u-x-ranges');
var defsEnter = wrapEnter.append('defs');
var gEnter = wrapEnter.append('g').attr('class', 'u-bars');
var g = wrap.select('g');
defsEnter.append('clipPath')
.attr('id', 'nv-edge-clip-' + id)
.append('rect');
wrap.select('#nv-edge-clip-' + id + ' rect')
.attr('width', availableWidth)
.attr('height', availableHeight);
gEnter.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
var bars = wrap.select('.u-bars').selectAll('g.u-bar').data(function(d) {
return d.values;
});
var barsEnter = bars.enter().append('g').attr('class', 'u-bar');
bars.attr('transform', function(d, i, j) {
return 'translate(' + xScale(d.from) + ', 0)';
});
barsEnter.append('rect');
barsEnter.append('text').attr('text-anchor', 'start');
bars.select('text')
.text(function(d, i) {
return d.label;
})
.attr('y', availableHeight - 5)
.attr('x', 5);
bars.selectAll('.u-bar rect').attr('height', yRange[0])
.attr('width', function(d, i, j) {
return xScale(d.to) - xScale(d.from);
});
bars.exit().remove();
};
};
})
.factory('nvd3XMarkersPlugin', function() {
return function nvd3XMarkersPlugin() {
var id = Math.floor(Math.random() * 10000);
return function nvd3XMarkersRender(chart, options, data) {
var xAxis = chart.xAxis || chart.xAxis1 || chart.x1Axis;
var yAxis = chart.yAxis || chart.yAxis1 || chart.y1Axis;
var axis = d3.select(chart.container).selectAll('g.nv-axis');
var container = d3.select(axis[0][0].parentNode);
var clipEdge = true;
var xScale = xAxis.scale();
var yScale = yAxis.scale();
var yRange = yAxis.range();
var xRange = xAxis.range();
var availableWidth = xRange[1];
var availableHeight = yRange[0];
var wrap = container.selectAll('g.u-wrap.u-x-markers').data([data]);
var wrapEnter = wrap.enter().insert('g', '.nv-interactive').attr('class', 'nvd3 u-wrap u-x-markers');
var defsEnter = wrapEnter.append('defs');
var gEnter = wrapEnter.append('g').attr('class', 'u-markers');
var g = wrap.select('g');
defsEnter.append('clipPath')
.attr('id', 'nv-edge-clip-' + id)
.append('rect');
wrap.select('#nv-edge-clip-' + id + ' rect')
.attr('width', availableWidth)
.attr('height', availableHeight);
gEnter.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
var markers = wrap.select('.u-markers').selectAll('g.u-marker').data(function(d) {
return d.values;
});
var markersEnter = markers.enter().append('g').attr('class', 'u-marker');
markers.attr('transform', function(d, i, j) {
return 'translate(' + xScale(d.x) + ', 0)';
});
markersEnter.append('line');
markersEnter.append('text').attr('text-anchor', 'start');
markers.select('text')
.text(function(d, i) {
return d.label;
})
.attr('dy', '1.2em')
.attr('dx', '0.35em')
.call(getBB);
markersEnter.insert("rect", "text");
markers.select('rect')
.attr("width", function(d) {
return d.bbox.width + d.bbox.x * 2;
})
.attr("height", function(d) {
return d.bbox.height + d.bbox.y * 2;
});
function getBB(selection) {
selection.each(function(d) {
d.bbox = this.getBBox();
});
}
markers.selectAll('.u-marker line')
.attr('y1', yRange[0])
.attr('x1', 0)
.attr('y2', yRange[1])
.attr('x2', 0);
markers.exit().remove();
};
};
});
})(window);