ng-d3

by g

HTML

<div ng-controller="Ctrl">
    <div d3>
        <div class="node"
            d3-enter="node in nodes"
            d3-classed="{ collapsed: node.collapsed }"
            d3-style="{ opacity: 0 }"
            d3-on-click="toggleChildren(node)">
            <span class="text name">{{ node.Name }}</span>
            <div class="node-manager">
                <span class="text manager pointer"
                    d3-on-click="openContact(node.Manager)">{{ node.Manager.Name }}</span>
                <div class="picture">
                    <img d3-attr-src="node.Manager.Picture" />
                </div>
            </div>
            <div class="node-contacts">
                <ul class="contact-list">
                    <li class=""
                        d3-enter="contact in (node.ResponsiblePersons || []).concat(node.Contacts || [])">
                    </li>
                </ul>
            </div>
        </div>
    </div>
</div>    
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"></script>

CSS

.node {
    display: block;
    height: 30px;
    width: 30px;
    border: solid 1px #ccc;
}

JavaScript

var module = angular.module('app', []);
module.directive("d3", function(){
    return {
        terminal: true,
        link: function(scope, element, attrs) {
            var node = element.clone();
            element.html('');
            
            var $root = d3.select(element[0]);
            var children = node.children();
            for (var childIdx = 0; childIdx < children.length; childIdx++) {
                var childNode = children[childIdx];
                var attrMap = {};
                for (var attrIdx = 0; attrIdx < childNode.attributes.length; attrIdx++) {
                    var attr = childNode.attributes[attrIdx];
                    if (attr.specified) {
                        attrMap[attr.name] = attr.value;
                    }
                }
                console.log({ node: childNode }, attrMap);
                var d3Enter = attrMap['d3-enter'];
                if (d3Enter) {
                    var tagName = childNode.tagName;
                    var className = childNode.className;
                    var selector = tagName;
                    if (className) {
                        selector = selector + "." + childNode.className; // TODO: handle multiple classes
                    }
                    var $child = $root.selectAll(selector)
                        .data(scope.nodes);
                    var $childEnter = $child.enter()
                        .append(tagName)
                        .classed(className, true);
                }
            }
                
            // console.log(element.html());
        }
    };
});

module.controller('Ctrl', function($scope) {
    $scope.nodes = [
        { name: 'Guillaume' },
        { name: 'Marx' }
    ];
});

angular.bootstrap(document.body, ['app']);