将d3 svg附加到当前的ng-repeat元素
Append d3 svg to current ng-repeat element
我想为每个使用ng-repeat生成的li元素附加一个d3.js饼图。
<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>
我的美元范围。Hashtag是包含Hashtag参与属性的对象数组:
[{
"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
}]
坦克到ng-重复,每次调用饼图指令时,我只传递一个h对象:
{
"Favorites": 2,
"Frequency": 1,
"Hashtag": "Alpes",
"Replies": 0,
"Retweets": 1,
"Engagement":3,
"tweetId": 615481266348146700
}
然后我手动将其"映射"为该格式:
var mapped = [{
"label": "Retweets",
"value": data.Retweets
}, {
"label": "Favorites",
"value": data.Favorites
}, {
"label": "Replies",
"value": data.Replies
}];
最后,我希望我的指令将饼添加到当前<div class="pie_chart"></div>
(在指令模板中生成),并使用已传递的当前h对象的映射数据。但是正如ocket-san所提到的,d3.select(someElement)
只匹配DOM中的第一个元素。
这是我的指令:
.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
};
}
}
}]);
问题是指令
var vis = d3.select(".pie_chart")
.append("svg:svg")
使用pie_chart类将所有饼图附加到第一个div。
我试着把它改成d3.select(element)(…),但它不起作用。
有什么建议吗?
提前感谢!问:
你可以看到当前的输出:http://i61.tinypic.com/wqqc0z.png
问题是d3.select('.pie_chart')
选择了body中匹配此类的第一个元素,而不是在您的指令模板中。要实现这一点,您应该使用link
函数中提供的element
对象。在你的例子中:
var vis = d3.select(element[0]).select(".pie_chart").append("svg")...
我创建了一个简化的小提琴来展示这个。
希望能有所帮助。
当我们一起使用Angularjs
和d3js
时,我们需要更新d3.select('body')
选择,使其相对于指令使用d3.select(element[0])
而不是整个DOM。我们必须使用element[0]
而不是element的原因是因为element"是"一个jQuery而不是普通的DOM对象。执行element[0]
只会给我们一个普通的旧DOM元素。(我用引号表示"is",因为从技术上讲,它是一个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在数组中使用元素索引来确定唯一性,方法是添加track by$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>
我发现这里的答案对我来说是不正确的。
Jarandaf -是最接近的,但我的解决方案是删除类选择器。
,只使用下面的代码:
d3.select(element[0]).append('svg')
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标签,并添加一个标签到每个div类"pie_chart",你需要这样做:
var piecharts = d3.selectAll(".pie_chart").append("svg");
结果将是一个类似上面的html结构。
如果这不是你想要的,那么我很抱歉,我想我完全误解了这个问题:-)
谢谢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>
感谢大家的帮助
问:
- 使用 ng-repeat访问 ng 表单元素/值
- AngularJS:根据其他对象预先选择ng repeat中的select元素
- Angular js将只显示ng repeat中的第一个元素
- 带有 ng-repeat 的 Angular JS 指令无法遍历子元素
- $compile和ng repeat未返回正确的元素
- 使用ng repeat时元素ID重复
- AngularJS:如何使用ng repeat来显示父数组中包含的数组的所有元素
- 如何使用ng repeat在匹配的元素上添加活动类
- 使用ng repeat将表单元素绑定到角度中的新对象
- 如何只更新ng repeat中的部分元素
- 操作NG-REPEAT的第一个元素
- AgularJS ng-repeat而不重复父元素
- 将 'ng-repeat' 元素传递给 javascript 函数
- 以编程方式在 ng-repeat元素上切换类
- 使用AngularJS指令来更改ng-repeat元素的类
- 如何强制jQuery“监听”?为将来的AngularJS ng-repeat元素激活插件
- Ng-click适用于所有ng-repeat元素
- ng-repeat元素# 39;索引在排序时改变
- 将d3 svg附加到当前的ng-repeat元素
- 我可以在javascript中声明ng-repeat元素吗?