Using D3.js with Angular 2

Angulard3.js

Angular Problem Overview


I have successfully integrated Angular 2 (Alpha 44) with D3.js:

<html>
<head>
<title>Angular 2 QuickStart</title>
<script src="../node_modules/systemjs/dist/system.src.js"></script>
<script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script>
  System.config({packages: {'app': {defaultExtension: 'js'}}});
  System.import('app/app');
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>

app.js:

/// <reference path="./../../typings/tsd.d.ts" />

import {Component, bootstrap, ElementRef} from 'angular2/angular2';

@Component({
  selector: 'my-app',
  template: '<h1>D3.js Integrated if background is yellow</h1>',
  providers: [ElementRef]
})
class AppComponent { 
  elementRef: ElementRef;

  constructor(elementRef: ElementRef) {
   this.elementRef = elementRef;
  }

afterViewInit(){
    console.log("afterViewInit() called");
    d3.select(this.elementRef.nativeElement).select("h1").style("background-color", "yellow");
  }
}
bootstrap(AppComponent);

Everything is working fine. But Angular 2 documentation for ElementRef states the following: >Use this API as the last resort when direct access to DOM is needed. Use templating and data-binding provided by Angular instead. Alternatively you take a look at {@link Renderer} which provides API that can safely be used even when direct access to native elements is not supported. Relying on direct DOM access creates tight coupling between your application and rendering layers which will make it impossible to separate the two and deploy your application into a web worker.

Now the question arises how to integrate D3.js with the Renderer API's?

Angular Solutions


Solution 1 - Angular

To use Renderer, you need the raw HTML element (aka nativeElement). So basically you have to use whatever your library is, get the raw element and pass it to Renderer.

For example

// h3[0][0] contains the raw element
var h3 = d3.select(this.el.nativeElement).select('h3');
this.renderer.setElementStyle(h3[0][0], 'background-color', 'blue');

The warning about ElementRef is about using it directly. That means that this is discouraged

elementRef.nativeElement.style.backgroundColor = 'blue';

But this is fine

renderer.setElementStyle(elementRef.nativeElement, 'background-color', 'blue');

For showing purposes you can use it as well with jQuery

// h2[0] contains the raw element
var h2 = jQuery(this.el.nativeElement).find('h2');
this.renderer.setElementStyle(h2[0], 'background-color', 'blue');

My recommendation though is to stick to use what angular2 provides you to do this easily without depending on another libraries.

With pure angular2 you have two easy ways

  • Using directives

// This directive would style all the H3 elements inside MyComp
@Directive({
	selector : 'h3',
	host : {
		'[style.background-color]' : "'blue'"
	}
})
class H3 {}

@Component({
	selector : 'my-comp',
	template : '<h3>some text</h3>',
	directives : [H3]
})
class MyComp {}
  • Using ViewChild with local variables

@Component({
	selector : 'my-comp',
	template : '<h3 #myH3>some text</h3>',
})
class MyComp {
	@ViewChild('myH3') myH3;
	ngAfterViewInit() {
		this.renderer.setElementStyle(this.myH3.nativeElement, 'background-color', 'blue');
	}
}

Here's a plnkr with all the cases I mentioned in this answer. Your requirements may differ, of course, but try to use angular2 whenever you can.

Solution 2 - Angular

Try this:

npm install [email protected] --save to set the version you need

npm install @types/[email protected] --save or a higher version if you want d3 4+

and then in your ts do

import * as d3 from 'd3';

Should work just fine

Solution 3 - Angular

npm install --save d3

check d3 version in package.json and check it in node_modules too.

then, in the component.ts, import it as below

import * as d3 from 'd3';

Solution 4 - Angular

I was having trouble using ElementRef, I'm not sure if that API has changed. So I ended up using ViewContainRef to get the nativeElement.

import {Component, ViewContainerRef, OnInit} from '@angular/core';
declare var d3:any;
@Component({
    selector: 'line-chart',
    directives: [],
    template: `<div class="sh-chart">chart</div>`
})
export class LineChart implements OnInit{
    elem ;
    constructor(private viewContainerRef:ViewContainerRef) {}
    ngOnInit(){
        this.elem = this.viewContainerRef.element.nativeElement;
        
        d3.select(this.elem).select("div").style("background-color", "yellow");
    };
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionZia KhanView Question on Stackoverflow
Solution 1 - AngularEric MartinezView Answer on Stackoverflow
Solution 2 - AngularPian0_M4nView Answer on Stackoverflow
Solution 3 - Angularuser7803780View Answer on Stackoverflow
Solution 4 - AngularHello DaveView Answer on Stackoverflow