2017-06-15 10 views
1

D3.jsナビゲーションバーを作成する角度(angular2/angular4ではなくangularJS)コンポーネントを構築しています。私は他のD3チャートに問題はありませんでしたが、最初はうまくレンダリングされましたが、 "ブラシ"が実行されたときにクラス変数の1つにアクセスしようとするとランタイムエラーが発生します。 (サイドノート:一般的なJavascriptのD3ブラシの実装例については、この参照:https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172'未定義は関数ではありません'実行時に角2/Typescript関数

を誤りとはいえ角度/活字体ではなくD3に関連すると表示されます。 「未定義は関数ではありません」にアクセスしようとすると "下のコードの "brushed()"関数の "this.x"を参照してください。

誰もが、私はそれが重要な場合は、「ブラッシング()」機能

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)); 
    } 


} 

に「this.x」と「this.x.invert」にアクセスできるようにするには何をする必要があるか説明することができますデータは単に日単位の在庫エントリの配列です。

[{日付、オープン、ハイ、ロー、クローズ} ...] {"date": "2017-06-07 13:02: 「72.0700」、「72.7700」、「71.9500」、「72.0800」、「72.0800」、「volume」:「9247460」、「adjClose」:「72.6350」、 }

D3 likes "d"参照を使用する

+0

タイピングを追加しましたか?すなわち 'npm install --save @ types/d3' – Milo

+0

はい。私はD3.jsタイプをインストールしています。完全な 'd3'セットはtypings.d.tsで利用できます。ちょうど明らかである - チャートは実際にうまくレンダリングします。私が "ブラシ"を実行しようとするまで、エラーに当たるとは限りません。 –

答えて

1

注:この質問は実際にはthis oneの複製です。私は通常そのように閉じていますが、その質問は何が起こっているのか説明していないので、私はここで一発撮ります。


.callにコールバック関数を渡しています。そのコールバックd3では、変数thisが参照している内容が変更されました。あなたのクラスを参照するのではなく、選択したノードのgノードです。

2つの選択肢があります。一つ、ES6脂肪矢印機能でそれをラップ:

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

これはthis周りにクロージャを作成し、自分のクラスへの参照を保持します。

または、2つは、.bindを使用してこれを保持します。 .bindはこれを強制的に保存します。

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

ここにはこの件に関するexcellent readingがあります。

関連する問題