80

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?

Balázs
  • 2,929
  • 2
  • 19
  • 34
Zia Khan
  • 817
  • 1
  • 7
  • 4
  • Is this in any help? http://www.ng-newsletter.com/posts/d3-on-angular.html –  Nov 04 '15 at 07:33
  • I am also trying to get D3 to work with angular 2. In the example above, I can see that you reference the d3-script in you index.html, but I can´t see how you get a hold of the d3-variable in app.js? – user2915962 Nov 16 '15 at 06:08
  • 8
    @user2915962 - npm install d3 , ensure postinstall runs and d3.d.ts is created by tsd, then `import * as d3 from 'd3/d3';` – drew moore Dec 01 '15 at 08:33
  • There's a video mentioned in one of the comments here: http://stackoverflow.com/q/34704148/2050479 that is quite interesting – Michael Feb 10 '16 at 07:12
  • 1
    @urosjarc - That's Angular 1, which has a very different way of doing these sorts of things. – superluminary Aug 30 '16 at 12:25
  • Another related video https://skillsmatter.com/skillscasts/8434-using-d3-with-angular-2-by-aendrew-rininsland (jump to 19th minute if in a hurry) – Aurelio Oct 04 '16 at 10:53

4 Answers4

58

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.

Eric Martinez
  • 31,277
  • 9
  • 92
  • 91
  • 2
    So, for the example of using bootstrap's collapse plugin, would I be right in saying that `jQuery(this.el.nativeElement).collapse('show');` would be a perfectly acceptable way of instantiating the plugin? – garethdn Jan 22 '16 at 09:47
  • I guess the directive approach wouldn't work for dynamically appended h3s right? Example: http://plnkr.co/edit/U2lvYqtGvdf7kWoRg10N?p=preview – eko Apr 27 '17 at 06:12
6

Try this:

npm install d3@3.5.36 --save to set the version you need

npm install @types/d3@3.5.36 --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

Pian0_M4n
  • 2,505
  • 31
  • 35
  • using the latest d3 (v4) and based on angular 2 quickstart example i had to also add `'d3':'npm:d3'` to map and `'d3': {main:'build/d3.js', defaultExtension:'js'}` to packages in `systemjs.config.js` file. – Nikos Tsokos Jan 13 '17 at 14:27
  • Hi @Entrodus, do you mind to give me the exact install sequence, based on Quickstart? I tried to follow this, including the system's mod you proposed, but I'm still stuck (no ./build/d2.js, no ./build folder either). – Stéphane de Luca Feb 14 '17 at 00:01
  • @StéphanedeLuca: 1.Set up angular quickstart > 2.install d3 and typings for d3 version 4.4.0 > 3.added map and packages directives for d3 in system.config.js > 4.add import of d3 in angular component that uses d3 > 5.Victory. – Nikos Tsokos Feb 14 '17 at 12:20
4
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';
3

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");
    };
}
Hello Dave
  • 231
  • 2
  • 10