The complete code repository is provided below for all the superior developers who will skip reading this article. For all other mediocre developers (definitely not you) note that the code on this page is simplified for readability.

Source: https://github.com/lsharir/angular-d3-graph-example (recently updated to angular 5)

Demo: https://lsharir.github.io/angular-d3-graph-example/

How to easily make cool looking shit like this

Below I will present one approach to harness Angular and D3 together. We will walk through the following steps:

Initializing a project Interfacing d3 through angular Generate a simulation Bind the simulation’s data to the document through angular Bind user interactions to the graph Optimize performance by controlling change detection Go online and complain about angular versioning strategy

So open your terminals, fire up your editors and warm up the clipboard, we are diving into the code.

Application Structure

We will separate d3 related code and svg visual elements code. I will go into detail when the relevant files will be created, but for now, this is the expected structure of our app:

d3

|- models

|- directives

|- d3.service.ts visuals

|- graph

|- shared

Initializing an Angular application

Start an Angular application project. Angular 5, 4 or 2 the code below was tested on all of those.

If you don’t have one already, use angular-cli to quickly set one up:

npm install -g @angular/cli

Then start a new angular object:

ng new angular-d3-example

Your application got generated at the angular-d3-example folder. Use the ng serve command from its root to start development, the application should be served on localhost:4200 .

Initializing D3

Make sure to install d3 and its relevant types declarations:

npm install --save d3

npm install --save-dev @types/d3

Interfacing D3 through Angular

The correct way to use d3 (or any other library) within a framework is to interact with it through a customized interface, one which we will implement as classes, angular services and directives. By working like that, we separate the core functionalities we are introducing from the components that implement them, making our application structure more flexible and scalable, and isolating bugs in their proper environment.

Our D3 folder will end up looking like this:

d3

|- models

|- directives

|- d3.service.ts

models will provide us typing safety and robust instances of datum. directives will direct elements in how to implement d3 behaviors. d3.service.ts will expose all the methods to be used by either d3 models and directives or external application components.

Let’s start designing the d3 service.

This service will provide us with computational models and behaviors. The getForceDirectedGraph method will return a force directed graph instance. The applyZoomableBehaviour and applyDraggableBehaviour methods will let us bind user interactions with elements to corresponding behaviors.

Force Directed Graph

Let’s start creating our force directed graph class and its relevant models. Our graph consists of nodes and links, let’s define the appropriate models.

After defining the core models for our graph to manipulate, we continue to define the graph model itself.

Once these models are defined, we can update our getForceDirectedGraph method in our D3Service

Creating an instance of ForceDirectedGraph will return an instance

ForceDirectedGraph {

ticker: EventEmitter,

simulation: Object

}

It contains a running simulation with the data we provided, along with a ticker holding an event emitter that fires every time the simulation ticks, which we will use like this:

graph.ticker.subscribe((simulation) => {});

We’ll implement the rest of the methods of the D3Service later, we’re going to try and bind the simulation data into the document.

Binding the simulation

We got a hold of our ForceDirectedGraph instance. It holds an ever changing precious data of nodes and their connecting links. You could bind that data to the document the d3 way (like a savage):

function ticked() {

node

.attr("cx", function(d) { return d.x; })

.attr("cy", function(d) { return d.y; });

}

Luckily, this is the 21st century, humanity has evolved to use efficient data binding tools instead of mindlessly changing attributes on elements. This is where Angular’s .muscles { display: flex }

Intermezzo: SVG and Angular

SVG templating with Angular

SVG delayed implementation resulted in a restricting namespace separation within the html document. Which is why Angular cannot recognize declared SVG elements in our components templates (Unless they are visibly descendants of an <svg> tag).

To compile our svg elements properly, we must either:

Meticulously keep them visibly nested under the <svg> tag. prefix them with “svg” to let Angular know what’s up <svg:line>

SVG components with Angular

Assigning selectors to components in the SVG namespace will not work the usual way. They must be selected through an attribute selector

Note the svg prefix in the component’s template

End of intermezzo

Binding the simulation — visuals

Equipped with ancient svg knowledge, we can start creating visual components that will display our data. Isolated in a visuals folder, we will create shared components (to be potentially used by other graphs) and our primary graph folder, that will hold all the code required to display our force directed graph.

visuals

|- graph

|- shared

Graph visuals

Let’s create the root component that will generate the graph and bind it to the document. We pass along the nodes and links data as the components input variables.

<graph [nodes]="nodes" [links]="links"></graph>

The component takes the nodes and links and creates an instance of the ForceDirectedGraph

Node visual component

Next, let’s create the node visual component, it will display a circle and the id of the node.

Link visual component

On to the link visual component:

Behaviors

Getting back to the d3 part of the application, let’s start creating the directives and service methods required to add cool behaviors to the graph.

Behavior — pan and zoom

We create hooks for the pan and zoom behavior, so it can be easily used:

<svg #svg>

<g [zoomableOf]="svg">

<!-- nodes and links -->

</g>

</svg>

Behavior — draggable

For a draggable behavior, we want to provide the simulation so it can be paused while dragging is in progress.