我正在努力尝试使用Angular和D3js创建一个简单的饼图指令。我觉得我在这里遗漏了一些关于JavaScript的明显内容但是我错过了它。任何帮助将不胜感激。
Plunkr:http://plnkr.co/lFluzdfLOINBzEGVRHwA
我的最终目标是让一个对象充当我的控制器可以引用的模型,但是现在我只是从带有工厂的JSON文件中获取数据。我的问题是我可以控制记录所有数据,它看起来像你期望的那样。我也可以在HTML中使用把手,即{{company}},整个对象将被打印到屏幕上。
但行:
var arcs = svg.selectAll(".slice")
.data(pie);
始终抛出以下错误:
"Error: i is undefined
n@http://localhost:63342/explorer/vendor/d3/d3.min.js:4:21667
pa.data@http://localhost:63342/explorer/vendor/d3/d3.min.js:3:11196
link@http://localhost:63342/explorer/app/features/pieChart/pieChartDirective.js:34:1
K@http://localhost:63342/explorer/vendor/angular/angular.min.js:54:390
A/<@http://localhost:63342/explorer/vendor/angular/angular.min.js:61:129
r/f.success/<@http://localhost:63342/explorer/vendor/angular/angular.min.js:72:199
ye/e/l.promise.then/J@http://localhost:63342/explorer/vendor/angular/angular.min.js:99:449
ye/e/l.promise.then/J@http://localhost:63342/explorer/vendor/angular/angular.min.js:99:449
ye/g/<.then/<@http://localhost:63342/explorer/vendor/angular/angular.min.js:101:116
Zd/this.$get</k.prototype.$eval@http://localhost:63342/explorer/vendor/angular/angular.min.js:111:342
Zd/this.$get</k.prototype.$digest@http://localhost:63342/explorer/vendor/angular/angular.min.js:108:411
Zd/this.$get</k.prototype.$apply@http://localhost:63342/explorer/vendor/angular/angular.min.js:112:171
h@http://localhost:63342/explorer/vendor/angular/angular.min.js:72:441
w@http://localhost:63342/explorer/vendor/angular/angular.min.js:77:347
we/</z.onreadystatechange@http://localhost:63342/explorer/vendor/angular/angular.min.js:78:420
" "<pie-chart-directive class="ng-binding">" angular.min.js:92
以下是所有相关代码:
的index.html
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<title>Explorer</title>
<link rel="stylesheet" href="vendor/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="vendor/bootstrap/dist/css/bootstrap-theme.min.css">
</head>
<body>
<div class="container-fluid">
<div class="row">
<h1>Data Explorer</h1>
</div>
<div class="row">
Path Navigator
</div>
<div class="row" ng-controller="PieChartController">
<pie-chart-directive></pie-chart-directive>
</div>
</div>
<!-- framework scripts -->
<script src="vendor/angular/angular.min.js"></script>
<script src="vendor/angular-route/angular-route.min.js"></script>
<script src="vendor/jquery/dist/jquery.min.js"></script>
<script src="vendor/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="vendor/d3/d3.min.js"></script>
<script src="vendor/topojson/topojson.js"></script>
<script src="vendor/lodash/dist/lodash.min.js"></script>
<script src="app/app.js"></script>
<!-- pieChart scripts -->
<script src="app/features/pieChart/pieChartModule.js"></script>
<script src="app/features/pieChart/PieChartController.js"></script>
<script src="app/features/pieChart/pieChartDirective.js"></script>
<!-- navigator scripts -->
<script src="app/features/navigator/navigatorModule.js"></script>
<script src="app/features/navigator/NavigatorController.js"></script>
<script src="app/features/navigator/navigatorDirective.js"></script>
</body>
</html>
app.js
angular.module('d3', []).factory('d3Service', [function() {
var d3;
return d3;
}]);
angular.module('lodash', []).factory('lodashService', [function() {
var _;
return _;
}]);
angular.module('myApp', [
'ngRoute',
'd3',
'lodash',
'pieChart',
'navigator'
]);
angular.module('myApp').factory('Company', ['$http', function($http) { 'use strict';
function Company(companyData) {
if(companyData) {
this.setData(companyData);
}
};
Company.prototype = {
setData: function(companyData) {
angular.extend(this, companyData); // Copies the properties of the second arg obj to the first arg obj
},
load: function() {
var scope = this;
return $http.get('app/data.json'); // Return this and use .then() in the controller to avoid using $watch
/*$http.get('app/data.json').success(function(data) {
scope.setData(data);
})
.error(function(err) {
console.log("Error getting data in Company.");
});*/
}
};
return Company;
}]);
pieChartModule.js
angular.module('pieChart', [
'd3',
'lodash'
]);
PieChartController.js
angular.module('pieChart')
.controller('PieChartController', ['$scope', 'Company', function($scope, Company) { 'use strict';
$scope.company = new Company();
//$scope.company.load();
$scope.company.load().then(function(d) {
$scope.company.setData(d.data);
});
}]);
pieChartDirective.js
angular.module('pieChart')
.directive('pieChartDirective', ['d3Service', function(d3Service) { 'use strict';
function link(scope, element, attrs) {
var initializing = true;
var w = 500, //width
h = 500, //height
r = 200, //radius
rP = 248,
color = d3.scale.category20c(); //builtin range of colors
//Set margins, width, and height
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var svg = d3.select(element[0])
.append("svg:svg")
.data([scope.company.campaigns])
.attr("width", w)
.attr("height", h);
var arc = d3.svg.arc()
.outerRadius(r);
var popOut = d3.svg.arc()
.outerRadius(rP);
var pie = d3.layout.pie()
.value(function(d) { return d.budget; })
.sort(null);
//scope.render = function() {
var arcs = svg.selectAll(".slice")
.data(pie);
arcs.enter().append("path")
.attr("class", "slice")
.attr("fill", function (d, i) { return color(i); })
.attr("d", arc)
.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")")
.on('mouseenter', function(d, i) {
d3.select(this)
.transition()
.duration(300)
.attr("d", popOut)
.style("opacity", 1)
.style("stroke", "black");
var tooltip = d3.select("body").append("div")
.attr("id", "tooltip")
.attr("class", "badge")
.style("position", "absolute")
.style("left", (d3.event.pageX-100 + "px"))
.style("top", (d3.event.pageY-100 + "px"))
.html("<h4>" + d.data.campaignName + "</h4>");
// Call digest when a change has been made that Angular is not automatically
// informed of (dirty-checking)
scope.$digest();
})
.on('mouseout', function(d, i) {
d3.select(this)
.transition()
.duration(300)
.attr("d", arc)
.style("stroke", "none")
.duration(300)
.style("opacity", .8);
$('#tooltip').remove();
});
arcs.transition().duration(1500).attrTween("d", tweenPie); // Original transition
function tweenPie(b) {
b.innerRadius = 0;
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) { return arc(i(t)); };
}
function change() {
if(!initializing) {
pie.value(function (d) { return d.budget; }); // change the value function
arcs = arcs.data(pie); // compute the new angles
arcs.transition()
.attr("d", arc);
}
}
//}
// Use true for 'objectEquality' property so comparisons are done on equality and not reference
/*scope.$watchCollection('company.campaigns', function(value) {
if(value) {
scope.render();
// Don't change this flag until all watched variables have been set
initializing = false;
}
}, true);*/
};
return {
restrict: 'E',
link: link,
templateUrl: 'app/features/pieChart/pieChart.html'
}
}]);
pieChart.html
<p>Pie Chart</p>