我想将d3.js饼图附加到使用ng-repeat生成的每个li元素。
@interface ObjectiveCClass : UIViewController {
int someVarialbe;
}
- (void)someFunction;
- (void)dealloc;
@end
@implementation ObjectiveCClass
- (void)someFunction{
//Log of someFunction
}
- (void)dealloc {
//Log line of objective-c dealloc
[super dealloc];
}
@end
class CPlusPlusClass{
ObjectiveCClass obj; // have a objective c member
CPlusPlusClass(){
obj = [[ObjectiveCClass alloc] init];
~CPlusPlusClass(){
//Log line of C++ class destructor
obj.someFunction;
[obj release];
}
};
我的$ scope.hashtag是一个包含主题标签参与属性的对象数组:
<ol>
<li ng-repeat="h in hashtags | orderBy:predicate:reverse | limitTo: limit">
<div class="hashtag">
<a ng-click="showTweetsForHashtag(h)">#{{h.Hashtag}}</a>
</div>
<div class="frequency">
{{h.Frequency}} times
</div>
<div class="engagement">
{{h.Engagement}}
<pie-chart data="h" on-click="showTweetsForHashtag(item)"></pie-chart>
</div>
</li>
</ol>
对于ng-repeat的坦克,每次调用饼图指令时,我只传递一个h对象:
[{
"Favorites": 0,
"Frequency": 1,
"Hashtag": "19h30",
"Replies": 0,
"Retweets": 1,
"Engagement":2,
"tweetId": 615850479952785400
}, {
"Favorites": 0,
"Frequency": 1,
"Hashtag": "80s",
"Replies": 0,
"Retweets": 2,
"Engagement":2,
"tweetId": [
616521677275533300,
617319253738393600
]
}{
"Favorites": 1,
"Frequency": 1,
"Hashtag": "AloeBlacc",
"Replies": 0,
"Retweets": 1,
"Engagement":2,
"tweetId": 617309488572420100
}, {
"Favorites": 2,
"Frequency": 1,
"Hashtag": "Alpes",
"Replies": 0,
"Retweets": 1,
"Engagement":3,
"tweetId": 615481266348146700
}]
然后我手动&#34;映射&#34;进入那种格式:
{
"Favorites": 2,
"Frequency": 1,
"Hashtag": "Alpes",
"Replies": 0,
"Retweets": 1,
"Engagement":3,
"tweetId": 615481266348146700
}
最后,我希望我的指令将pie添加到当前var mapped = [{
"label": "Retweets",
"value": data.Retweets
}, {
"label": "Favorites",
"value": data.Favorites
}, {
"label": "Replies",
"value": data.Replies
}];
(在指令模板中生成)中,并使用已传递的当前h对象的映射数据。但正如ocket-san提到的<div class="pie_chart"></div>
只匹配DOM中的第一个元素。
这是我的指示:
d3.select(someElement)
问题在于指令
.directive('pieChart', ['d3', function(d3) {
return {
restrict: 'E',
scope: {
data: '=',
onClick: '&'
},
template: '<div class="pie_chart"></div>',
link: function(scope, iElement, iAttrs) {
// watch for data changes and re-render
scope.$watch('data', function(newVals, oldVals) {
if (newVals) {
scope.render(newVals);
}
}, true);
scope.render = function(data) {
var w = 50, //width
h = 50, //height
r = data.Engagement / 3, // adapt radius to engagement value
color = d3.scale.ordinal().range(["#77b255", "#ffac33", "#07c"]); //custom range of colors
// map data to to be used by pie chart directive
var mapped = [{
"label": "Retweets",
"value": data.Retweets
}, {
"label": "Favorites",
"value": data.Favorites
}, {
"label": "Replies",
"value": data.Replies
}];
data = mapped;
// Courtesy of https://gist.github.com/enjalot/1203641
var vis = d3.select(".pie_chart")
.append("svg:svg") //create the SVG element inside the <body>
.data([data]) //associate our data with the document
.attr("width", w) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", h)
.append("svg:g") //make a group to hold our pie chart
.attr("transform", "translate(" + r + "," + r + ")") //move the center of the pie chart from 0, 0 to radius, radius
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.outerRadius(r);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.value(function(d) {
return d.value;
}); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr("class", "slice"); //allow us to style things in the slices (like text)
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
}) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function
};
}
}
}]);
使用pie_chart类将所有饼图附加到第一个div。
我尝试将其更改为d3.select(iElement)(...)但它没有用。
有什么建议吗?
提前致谢! Q值。
你可以在那里看到当前的输出: http://i61.tinypic.com/wqqc0z.png
答案 0 :(得分:3)
问题在于ViewTreeObserver.OnGlobalLayoutListener
选择与正文中的此类匹配的第一个元素,而不是在指令模板中。要实现此目的,您应该使用onWindowFocusChanged()
函数中提供的d3.select('.pie_chart')
对象。在你的情况下:
element
我创建了一个简化的fiddle试图展示这个。
希望它有所帮助。
答案 1 :(得分:2)
当我们一起使用Angularjs
和d3js
时,我们需要更新d3.select('body')
选项以使用相对于指令
d3.select(element[0])
而不是整个DOM。我们必须使用element[0]
而不仅仅是元素的原因是因为元素“是”jQuery
包装选择而不是普通的DOM对象。做element[0]
只给我们一个简单的旧DOM元素。 (我在引号中说“是”因为它在技术上是一个jqlite包装的DOM元素.jqlite本质上是jQuery的精简版。)
因此,您需要将代码更新为:
.directive('pieChart', ['d3', function(d3) {
return {
restrict: 'E',
scope: {
data: '=',
onClick: '&'
},
template: '<div class="pie_chart"></div>',
link: function(scope, iElement, iAttrs) {
// watch for data changes and re-render
scope.$watch('data', function(newVals, oldVals) {
if (newVals) {
scope.render(newVals);
}
}, true);
scope.render = function(data) {
var w = 50, //width
h = 50, //height
r = data.Engagement / 3, // adapt radius to engagement value
color = d3.scale.ordinal().range(["#77b255", "#ffac33", "#07c"]); //custom range of colors
// map data to to be used by pie chart directive
var mapped = [{
"label": "Retweets",
"value": data.Retweets
}, {
"label": "Favorites",
"value": data.Favorites
}, {
"label": "Replies",
"value": data.Replies
}];
data = mapped;
// Courtesy of https://gist.github.com/enjalot/1203641
//Part need Update
var vis = d3.select(iElement[0])
.append("svg:svg") //create the SVG element inside the <body>
.data([data]) //associate our data with the document
.attr("width", w) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", h)
.append("svg:g") //make a group to hold our pie chart
.attr("transform", "translate(" + r + "," + r + ")") //move the center of the pie chart from 0, 0 to radius, radius
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.outerRadius(r);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.value(function(d) {
return d.value;
}); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr("class", "slice"); //allow us to style things in the slices (like text)
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
}) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function
};
}
}
}]);
当您更新代码时,directive('pieChart')
功能会动态选择<pie-chart/>
代码。如果您有特定的课程,请将您的代码更新为:
var vis = d3.select(iElement[0]).select(".pie_chart")
更新1
您需要将$index
添加到ng-repeat
,因为:
Angular告诉我们的是
ng-repeat
中的每个元素都必须是唯一的。然而, 我们可以告诉Angular使用数组中的元素索引,而不是通过$index
添加跟踪来确定唯一性。
<ol>
<li ng-repeat="h in hashtags track by $index" | orderBy:predicate:reverse | limitTo: limit">
<div class="hashtag">
<a ng-click="showTweetsForHashtag(h)">#{{h.Hashtag}}</a>
</div>
<div class="frequency">
{{h.Frequency}} times
</div>
<div class="engagement">
{{h.Engagement}}
<pie-chart data="h" on-click="showTweetsForHashtag(item)"></pie-chart>
</div>
</li>
</ol>
答案 2 :(得分:2)
我发现这里的答案在我的案例中是不正确的。
Jarandaf - 是最接近但我的解决方案是删除类选择器。
并使用以下代码:
d3.select(element[0]).append('svg')
答案 3 :(得分:0)
d3.select(“element”)始终选择它找到的第一个元素。例如:假设您有以下html结构:
<body>
<p></p>
<p></p>
<p></p>
</body>
你会编码:d3.select(“p”)。append(“svg”),结果将是
<body>
<p>
<svg></svg>
</p>
<p></p>
<p></p>
</body>
您需要使用 d3.selectAll(element),这将为您提供包含适合选择器的所有项目的d3选择。
编辑:
好的,所以我认为你的最终html结构看起来像这样:
<ol>
<li ng-repeat="h in hashtags | orderBy:predicate:reverse | limitTo: limit">
<div class="hashtag">
<a ng-click="showTweetsForHashtag(h)">#{{h.Hashtag}}</a>
</div>
<div class="frequency">
{{h.Frequency}} times
</div>
<div class="engagement">
{{h.Engagement}}
<div id="pie_chart">
<svg> your piechart goes here</svg>
</div>
</div>
</li>
<li ng-repeat="h in hashtags | orderBy:predicate:reverse | limitTo: limit">
<div class="hashtag">
<a ng-click="showTweetsForHashtag(h)">#{{h.Hashtag}}</a>
</div>
<div class="frequency">
{{h.Frequency}} times
</div>
<div class="engagement">
{{h.Engagement}}
<div id="pie_chart">
<svg> another piechart goes here</svg>
</div>
</div>
</li>
</ol>
所以假设html结构已经存在而没有标签(因为我不知道任何关于角度或指令的事情:-))并且你想要附加svg标签并将一个标签附加到每个具有类“pie_chart”的div,你需要这样做:
var piecharts = d3.selectAll(".pie_chart").append("svg");
结果将是上面的html结构。
如果这不是你想要的,那么我很抱歉,我想我完全误解了这个问题: - )
答案 4 :(得分:0)
谢谢Gabriel的回答!
与此同时,我找到了一个解决方法(它可能不是最漂亮的,但它确实有效!)
指令:
.directive('pieChart', ['d3', function(d3) {
return {
restrict: 'E',
scope: {
data: '=',
max: '@',
item: '@',
onClick: '&'
},
template: '<div class="pie_chart"></div>',
link: function(scope, iElement, iAttrs) {
// watch for data changes and re-render
scope.$watch('data', function(newVals, oldVals) {
if (newVals) {
scope.render(newVals);
}
}, true);
scope.render = function(data) {
// Courtesy of https://gist.github.com/enjalot/1203641
var vis = d3.selectAll(".pie_chart")
.each(function(d, i) {
if (scope.item == i) {
var w = 50, //width
h = 50, //height
normalized = 50 * (data.Engagement) / (scope.max),
r = normalized/2, // adapt radius to engagement value
color = d3.scale.ordinal().range(["#77b255", "#ffac33", "#07c"]); //custom range of colors
// map data to to be used by pie chart directive
var mapped = [{
"label": "Retweets",
"value": data.Retweets
}, {
"label": "Favorites",
"value": data.Favorites
}, {
"label": "Replies",
"value": data.Replies
}];
var vis = d3.select(this)
.append("svg:svg") //create the SVG element inside the template
.data([mapped]) //associate our data with the document
.attr("width", w) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", h)
.append("svg:g") //make a group to hold our pie chart
.attr("transform", "translate(" + (w/2) + "," + (h/2) + ")") //move the center of the pie chart from 0, 0 to radius, radius
.on("click", function(d, i){
return scope.onClick({item: data});
});
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.outerRadius(r);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.value(function(d) {
return d.value;
}); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr("class", "slice"); //allow us to style things in the slices (like text)
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
}) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function
}
})
};
}
}
}])
HTML
<ol>
<li ng-repeat="h in hashtags | orderBy:predicate:reverse | limitTo: limit">
<div class="hashtag">
<a ng-click="showTweetsForHashtag(h)">
#{{h.Hashtag}}
</a>
</div>
<div class="frequency">
{{h.Frequency}} times
</div>
<div class="engagement">
<pie-chart data="h" max="{{hashtagMaxEngagement}}" item="{{$index}}" on-click="showTweetsForHashtag(item)">
</pie-chart>
</div>
</li>
</ol>
感谢大家的帮助!
Q值。