web-dev-qa-db-ja.com

生成されたd3htmlでangularjsディレクティブを使用するにはどうすればよいですか?

私はd3ビジュアライゼーションでangularjs tooltipディレクティブ を使用しようとしているので、次のようなものがあります

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も、それはうまくいきませんでした。

25
zlog

私も同様の問題を抱えていましたが、はい、$compileで解決しました。 d3コードがカスタムディレクティブ内にあると想定しています。そこから、ツールチップ属性を追加し、カスタムディレクティブ属性を削除して$ compileが1回だけ実行されるようにし、$ 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サービスは、ディレクティブによって追加された属性を使用して要素がコンパイルされていることを確認します。

これが機能するフィドルです 上記のコードを使用します。それがあなたが探しているものであることを願っています!

19
jbll

@jbllからのかなり良い答え-しかし、ディレクティブのコンパイルをenterフェーズの終わりにチェーンするのがおそらく最善でしょう。グラフィックがすべての要素を再作成せずにデータの更新に応答できるように、入力フェーズと更新フェーズを用意することが重要です。前の答えでは、モデルが変更されるたびに、すべてのノードのすべてのディレクティブがコンパイルされていました。これは望まれていることかもしれませんが、おそらくそうではありません。

次のコードは、$ scope.nodes変数が変更されるたびにd3グラフィックが更新されることを示しています。

これは、元のディレクティブを削除して再作成する必要がないため、少しすっきりしています。これは、ちょっとしたハックのようです。

これがフィドルです

ボタンをhtmlに追加します。

<button ng-click="moveDots()">Move the dots</button>

次に、JavaScriptファイルを次のように変更します。

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)
            }
        }
    };
}]);
17
david004

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);
        }
    };
}]);
2
NTyler

htmlがangularjs以外のものによって生成され、DOMに挿入される場合は、ディレクティブ属性を含むhtmlをコンパイルしてから、DOMに挿入して、angularがそれを認識できるようにする必要があります) 。

2
btm1

@ david004は、 enter() へのチェーンについて良い点を示しているため、 $compile は各入力要素で1回だけ呼び出されます。しかし、parentNode$compileを呼び出す代わりに、次のように使用しています call to $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 );
  } );
}
0
Philip Bulley