2017-06-15 6 views
1

Je construis un composant angulaire (angulaire2/angulaire4 non angulaireJS) qui crée une barre de navigation D3.js. Je n'ai pas eu de problèmes avec les autres diagrammes D3, et cela fonctionne très bien au début, mais j'ai une erreur d'exécution lorsque j'essaie d'accéder à l'une des variables de la classe quand une "brosse" est exécutée. (Note: voir par un exemple de mise en œuvre de la brosse générale Javascript D3: https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172)erreur 'non défini n'est pas une fonction' à l'exécution avec la fonction Angular 2/Typescript

L'erreur semble être liée à angulaire/Tapuscrit et non D3 si: « undefined est pas une fonction » en essayant d'accéder " this.x "dans le" brossé() "fonction dans le code ci-dessous (près du fond).

Quelqu'un peut-il expliquer ce que je dois faire pour être en mesure d'accéder à « this.x » et « this.x.invert » dans le « brossé() » fonction

import { Component, OnInit, OnChanges, ViewChild, ElementRef, Input, ViewEncapsulation } from '@angular/core'; 
import * as d3 from 'd3'; 
import {StockData} from "../../dataServices/stockData"; 

@Component({ 
    selector: 'app-navchart', 
    templateUrl: './navchart.component.html', 
    styleUrls: ['./navchart.component.css'], 
    encapsulation: ViewEncapsulation.None 
}) 
export class NavchartComponent implements OnInit, OnChanges { 
    @ViewChild('chart') public chartContainer: ElementRef; 
    @Input() public stockdata: StockData; 

    public margin: any = { top: 20, bottom: 20, left: 20, right: 20}; 
    public width : number; 
    public height: number; 
    public svg: any; 
    public g: any; 
    public chart: any; 
    public x: any; 
    public y: any; 
    public navline: any; 
    public navarea: any; 
    public data: any; 
    public brush: any; 


    constructor() { } 

    ngOnInit() { 
    console.log("Inside the charting - updating the data"); 
    if (this.stockdata) { 
     //console.log(JSON.stringify(this.stockdata)); 

     this.data = this.stockdata.stocklist; 

     setTimeout(() => { 
     this.initChart(); 
     this.drawAxis(); 
     this.drawRange(); 
     }, 500); 
    } 
    } 

    ngOnChanges() { 

    } 

    public initChart(): void { 
    let element = this.chartContainer.nativeElement; 
    this.width = element.offsetWidth - this.margin.left - this.margin.right; 
    this.height = element.offsetHeight - this.margin.top - this.margin.bottom; 

    this.svg = d3.select(element).append('svg') 
     .attr('width', element.offsetWidth) 
     .attr('height', element.offsetHeight); 

    this.g = this.svg.append('g') 
     .attr("transform", "translate(" + this.margin.left + "," + this.margin.top +")"); 


    // x and y scale functions - called every time a value needs converted to pixel location 
    //this will need moved to a "redraw" function when adjustments to overall chart size are allowed 
    this.x = d3.scaleTime() 
      .range([0, this.width]); 

    this.x.domain(d3.extent(this.data, (d: any) => new Date(d.date))); 

    this.y = d3.scaleLinear() 
     .range([this.height, 0]); 


     //sets the limits of x and y data. 
     // this will need to be moved to a redraw when changes to dataset ranges are allowed 
     this.y.domain([ 
     d3.min(this.data, (d: any) => d.close), 
     d3.max(this.data, (d: any) => d.close) 
     ]); 
     console.log ("Min = " + d3.min(this.data, (d: any) => d.close)); 

     // line drawing functions 
     this.navline = d3.line() 
     .curve(d3.curveBasis) 
     .x((d: any) => this.x(new Date(d.date))) 
     .y((d: any) => this.y(d.close)); 

     this.navarea = d3.area() 
     .curve(d3.curveBasis) 
     .x((d: any) => this.x(new Date(d.date))) 
     .y1((d: any) => this.y(d.close)) 
     .y0((d: any) => this.height); 



     this.g.append("g") 
     .attr("class", "brush") 
     .call(d3.brushX().on("end", this.brushed)); 
    } 


/* Error is in this function. It cannot find "this.x" from the class, 
    * and gives an undefined error. 
    * Right now the function is just setting debug content, but when 
    * this.x is working, I will add .invert() to it to get the original 
    * date values associated with the pixel location on the x-axis. 
    */ 
    public brushed(): void { 
    console.log(JSON.stringify(d3.event.selection)); 
    //returns proper [x0,x1] pixel values such as [102,500] on a svg 800 pixels wide. 

    let dat: any = d3.event.selection.map(this.x); 
    //let dat: any = d3.event.selection.map(this.x.invert) also fails with "invert does not exist on undefined" 
    console.log(JSON.stringify(dat)); 

    //The error appears to be because it can't find this.x, even though that is declared and works in 
    // other sections of the same class. 
    } 

    //draw x and y axes 
    public drawAxis(): void { 
     this.g.append("g") 
     .attr("class", "axis axis--x") 
     .attr("transform", "translate(0," + this.height + ")") 
     .call(d3.axisBottom(this.x)); 

    } 

    public drawRange(): void { 

     this.g.append("path") 
     .attr("class", "area") 
     .attr("d", this.navarea(this.data)); 


     this.g.append("path") 
     .attr("class", "line") 
     .attr("d", this.navline(this.data)); 
    } 


} 

S'il est important, les données sont simplement un tableau d'entrées de stock quotidiennes au format:

[{date, open, high, low, close} ...] {"date": "2017-06-07 13:02: 00 "," ouvert ":" 72.6350 "," haut ":" 72.7700 "," bas ":" 71.9500 "," fermer ":" 72.0800 "," volume ":" 9247460 "," adjClose ":" 72.6350 " }

D3 J'aime pour utiliser la référence "d"

+0

Avez-vous essayé d'ajouter des typings? c'est-à-dire 'npm install --save @ types/d3' – Milo

+0

Oui. J'ai les types D3.js installés. L'ensemble complet 'd3' est disponible dans typings.d.ts. Juste pour être clair - le tableau rend vraiment très bien. Ce n'est pas jusqu'à ce que j'essaie d'exécuter un "pinceau" qu'il frappe l'erreur. –

Répondre

1

Remarque, cette question est vraiment une copie de this one. D'habitude, je le ferme en tant que tel, mais cette question n'explique pas ce qui se passe, alors je vais essayer ici.


Vous transmettez une fonction de rappel à .call. Dans ce rappel d3 a changé ce que la variable this fait référence. Il ne fait plus référence à votre classe mais au noeud g de la sélection.

Vous avez donc deux choix. L'un, l'envelopper dans une fonction de flèche de graisse ES6:

.call(d3.brushX().on("end",() => this.brushed()));  

Cela crée une fermeture autour this et conserve la référence à votre classe.

Ou, deux, utilisez .bind pour préserver cela. .bind oblige la préservation de cela.

.call(d3.brushX().on("end", this.brushed.bind(this))); 

Voici quelques excellent reading sur ce sujet.