Подтвердить что ты не робот

Как использовать директивы angularjs в сгенерированном d3 html?

Я пытаюсь использовать angeljs подсказка подсказки на моей визуализации d3, поэтому у меня есть что-то вроде

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, но это не сработало.

4b9b3361

Ответ 1

У меня была аналогичная проблема, и да, решил ее с помощью $compile. Я предполагаю, что ваш код d3 находится в пользовательской директиве. Оттуда вы можете добавить свои атрибуты подсказки, удалить свой настраиваемый атрибут директивы, поэтому компиляция $запускается только один раз и вызывает $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);
            }
        };
    }]);

Служба компиляции $гарантирует, что ваш элемент скомпилирован с атрибутами, добавленными вашей директивой.

Вот рабочий скрипт, используя приведенный выше код. Надеюсь, это то, что вы ищете!

Ответ 2

Довольно хороший ответ от @jbll. Но, вероятно, лучше всего связать компиляцию директивы в конце фазы введите. Важно иметь фазу ввода и этап обновления, чтобы графический объект мог реагировать на обновления данных без повторного создания каждого элемента. В предыдущем ответе была бы всякая директива для каждого node, скомпилированного всякий раз, когда была изменена модель. Это может быть то, что нужно, но, вероятно, нет.

Следующий код показывает графическое обновление d3 всякий раз, когда изменяется переменная $scope.nodes.

Это также немного опрятно, потому что это не требует удаления и восстановления исходной директивы, которая кажется немного взломанной.

Вот скрипка

Добавьте кнопку в 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)
            }
        }
    };
}]);

Ответ 3

если html генерируется чем-то иным, чем angularjs и вставлен в DOM, вам нужно будет скомпилировать html, который включает ваши директивные атрибуты, прежде чем вставлять его в DOM, чтобы angular знал об этом.

Ответ 4

Мне нравится этот метод намного лучше, так как вам не нужно вызывать 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);
        }
    };
}]);

Ответ 5

@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 );
  } );
}