我正在尝试在我的d3可视化中使用angularjs tooltip directive,所以我有类似的东西
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("tooltip-append-to-body", true)
.attr("tooltip", function(d) {
return d.name;
})
// ... attributes
但是,工具提示未显示。我需要$compile
还是其他什么?我已经尝试将它包裹在$timeout
左右,但这不起作用。
答案 0 :(得分:19)
我遇到了类似的问题,是的,用$compile
解决了这个问题。我假设您的d3代码在自定义指令中。从那里你可以添加你的工具提示属性,删除你的自定义指令属性,所以$ compile只运行一次,并调用$ compile:
myApp.directive('myNodes', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var nodes = [{"name": "foo"}, {"name": "bar"}]
var mySvg = d3.select(element[0])
.append("svg")
.attr("width", 100)
.attr("height", 100);
var node = mySvg.selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("cx", function(d,i){
return 20+i*50;
})
.attr("cy", 50)
.attr("r", 10)
.attr("tooltip-append-to-body", true)
.attr("tooltip", function(d){
return d.name;
});
element.removeAttr("my-nodes");
$compile(element)(scope);
}
};
}]);
$ compile服务确保使用指令添加的属性编译元素。
Here is a working fiddle使用上面的代码。希望这就是你要找的东西!
答案 1 :(得分:17)
来自@jbll的一个非常好的答案 - 但最好将指令编译链接到输入阶段的末尾。具有输入阶段和更新阶段非常重要,因此图形可以响应数据更新而无需重新创建每个元素。无论何时更改模型,上一个答案都会在每个节点上编译每个指令。这可能是想要的,但可能不是。
以下代码显示$ scope.nodes变量更改时的d3图形更新。
这也有点整洁,因为它不需要删除和重新创建原始指令,这看起来有点像黑客。
将按钮添加到html:
<button ng-click="moveDots()">Move the dots</button>
然后将JavaScript fie更改为:
var myApp = angular.module('myApp', ['ui.bootstrap']);
myApp.controller('myCtrl', ['$scope', function($scope){
$scope.nodes = [
{"name": "foo", x: 50, y: 50},
{"name": "bar", x: 100, y: 100}
];
$scope.moveDots = function(){
for(var n = 0; n < $scope.nodes.length; n++){
var node = $scope.nodes[n];
node.x = Math.random() * 200 + 20;
node.y = Math.random() * 200 + 20;
}
}
}]);
myApp.directive('myNodes', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var mySvg = d3.select(element[0])
.append("svg")
.attr("width", 250)
.attr("height", 250);
renderDots();
scope.$watch("nodes", renderDots, true);
function renderDots(){
// ENTER PHASE
mySvg.selectAll("circle")
.data(scope.nodes)
.enter()
.append("circle")
.attr("tooltip-append-to-body", true)
.attr("tooltip", function(d){
return d.name;
})
.call(function(){
$compile(this[0].parentNode)(scope);
});
// UPDATE PHASE - no call to enter(nodes) so all circles are selected
mySvg.selectAll("circle")
.attr("cx", function(d,i){
return d.x;
})
.attr("cy", function(d,i){
return d.y;
})
.attr("r", 10);
// todo: EXIT PHASE (remove any elements with deleted data)
}
}
};
}]);
答案 2 :(得分:2)
如果html是由angularjs以外的东西生成并插入到DOM中,那么在将其插入DOM之前需要编译包含指令属性的html,以便angular知道它。
答案 3 :(得分:2)
我更喜欢这种方法,因为你不必调用removeAttr(看起来像是黑客)
myApp.directive('myNodes', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var nodes = [{"name": "foo"}, {"name": "bar"}]
var mySvg = d3.select(element[0])
.append("svg")
.attr("width", 100)
.attr("height", 100);
var node = mySvg.selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("cx", function(d,i){
return 20+i*50;
})
.attr("cy", 50)
.attr("r", 10)
.attr("tooltip-append-to-body", true)
.attr("tooltip", function(d){
return d.name;
});
$compile(svg[0])(scope);
}
};
}]);
答案 4 :(得分:0)
@ david004对链接到enter()
提出了一个很好的观点,因此$compile
仅在每个输入元素上调用一次。但是,不是在$compile
上调用parentNode
,而是我使用call到$compile
每个人进入元素的方式:
// Entering
myD3Selection.enter()
.append( 'rect' )
.attr( {foo: 'bar'} )
.call( compile );
// Compile encapsulated in reusable function, so can be used on multiple enter() chains
function compile( d3Selection )
{
d3Selection.each( function( d, i )
{
// this is the actual DOM element
$compile( this )( scope );
} );
}