d3 v4 TypeScript DefinitelyTyped在X轴上带有ScaleTime的Angular2线

时间:2017-06-16 23:08:41

标签: javascript angular typescript d3.js definitelytyped

我正在angular2组件中实现以下块(简单折线图): https://bl.ocks.org/d3noob/6f082f0e3b820b6bf68b78f2f7786084

我做过: npm install d3 --save npm install @types/d3 --save

我已经加载了d3.min.js和d3.node.js vi SystemJS。我在下面包含了我的component.ts文件。

import { Component, Input, ElementRef, OnInit } from '@angular/core';
import { TimelineChartConfig } from "./timeline";
import * as D3 from 'd3';


@Component({
  moduleId: module.id,
  selector: 'timeline',
  templateUrl: 'timeline.component.html',
  styles: [`
    :host {
        width: 100%;
        display:block;
    }
    :host .axis path,
    :host .axis line {
        fill: none;
        stroke: rgba(0, 0, 0, 0.2);
        color: rgba(0, 0, 0, 0.2);
        shape-rendering: crispEdges;
    }

    :host .axis text {
        font-size: 20px;
        fill: rgba(0, 0, 0, 0.9);
    }

    :host .color-label{
        display: inline;
    }
    :host(.timeline) {
        fill: none;
        stroke: steelblue;
        stroke-width: 2px;
     }
 `],
})

export class TimelineChartComponent implements OnInit {
  private host;        // D3 object referencing host dom object
  private svg;         // SVG in which we will print our chart
  private margin;      // Space between the svg borders and the actual chart graphic
  private width;       // Component width
  private height;      // Component height
  private xScale;      // D3 scale in X
  private yScale;      // D3 scale in Y
  private xAxis;       // D3 X Axis
  private yAxis;       // D3 Y Axis
  private htmlElement: HTMLElement; // Host HTMLElement
  config: Array<TimelineChartConfig> = [];

  /* Constructor, needed to get @Injectables */
  constructor(private element: ElementRef) {
    this.htmlElement = this.element.nativeElement;
    this.host = D3.select(this.element.nativeElement);
    let incomeTimeline = new TimelineChartConfig();
    incomeTimeline.settings = {
        interpolation: 'monotone'
    }
    let data = [
            { x: "1-May-12", y: 58.13},
            { x: "30-Apr-12", y: 53.98},
            { x: "27-Apr-12", y: 67.00},
            { x: "26-Apr-12", y: 89.70},
            { x: "25-Apr-12", y: 99.00},
            { x: "24-Apr-12", y: 130.28},
            { x: "23-Apr-12", y: 166.70},
            { x: "20-Apr-12", y: 234.98},
            { x: "19-Apr-12", y: 345.44},
            { x: "18-Apr-12", y: 443.34},
            { x: "17-Apr-12", y: 543.70},
            { x: "16-Apr-12", y: 580.13},
            { x: "13-Apr-12", y: 605.23},
            { x: "12-Apr-12", y: 622.77},
            { x: "11-Apr-12", y: 626.20},
            { x: "10-Apr-12", y: 628.44},
            { x: "9-Apr-12", y: 636.23},
            { x: "5-Apr-12", y: 633.68},
            { x: "4-Apr-12", y: 624.31},
            { x: "3-Apr-12", y: 629.32},
            { x: "2-Apr-12", y: 618.63},
            { x: "30-Mar-12", y: 599.55},
            { x: "29-Mar-12", y: 609.86},
            { x: "28-Mar-12", y: 617.62},
            { x: "27-Mar-12", y: 614.48},
            { x: "26-Mar-12", y: 606.98},
        ];
        incomeTimeline.dataset = data;
        this.config.push(incomeTimeline);
  }

  ngOnInit(): void {
     if (!this.config || this.config.length === 0) return;
        this.setup();
        this.buildSVG();
        this.populate();
        this.drawXAxis();
        this.drawYAxis();
  }

  /* Will setup the chart container */
  private setup(): void {
    this.margin = { top: 20, right: 20, bottom: 40, left: 40 };
    this.width = this.htmlElement.clientWidth - this.margin.left - this.margin.right;
    this.height = this.width * 0.5 - this.margin.top - this.margin.bottom;
    this.xScale = D3.scaleTime().range([0, this.width]);
    this.yScale = D3.scaleLinear().range([this.height, 0]);
  }

  /* Will build the SVG Element */
  private buildSVG(): void {
    this.host.html('');
    this.svg = this.host.append('svg')
      .attr('width', this.width + this.margin.left + this.margin.right)
      .attr('height', this.height + this.margin.top + this.margin.bottom)
      .append('g')
      .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
  }
  /* Will draw the X Axis */
  private drawXAxis(): void {
    this.xAxis = D3.axisBottom(this.xScale);
    this.svg.append('g')
      .attr('class', 'x axis')
      .attr('transform', 'translate(0,' + this.height + ')')
      .call(this.xAxis);
  }

  /* Will draw the Y Axis */
  private drawYAxis(): void {
    this.yAxis = D3.axisLeft(this.yScale)
      .tickPadding(10);
    this.svg.append('g')
      .attr('class', 'y axis')
      .call(this.yAxis)
      .append('text')
      .attr('transform', 'rotate(-90)');
  }

  /* Will get the Maximum value in Y */
  private getMaxY(): number {
    let maxValuesOfCharts = [];
    this.config.forEach(data => maxValuesOfCharts.push(Math.max.apply(Math, data.dataset.map(d => d.y))));
    return Math.max(...maxValuesOfCharts);
  }

  /* Will populate datasets into areas*/
  private populate(): void {
    let parseTime = D3.timeParse("%d-%b-%y");
    this.config.forEach((timeline: any) => {
      console.log("timeline config", timeline);
      timeline.dataset.forEach((d: any) => d.x = parseTime(d.x));
      this.xScale.domain(D3.extent(timeline.dataset, (d: any) => d.x));
      this.yScale.domain([0, this.getMaxY()]);

      let valueline = D3.line()
        .x((d) => this.xScale(d.x))
        .y((d) => this.yScale(d.y))

      this.svg.append('path')
        .datum(timeline.dataset)
        .attr('class', 'timeline')
        .attr('d', valueline);
    });
  }
}

这是@ types / d3-shape / index.d.ts的Line部分,我认为是适当的部分?但我不知道如何解释这一点。

export interface Line<Datum> {
    /**
     * Generates a line for the given array of data. Depending on this line generator’s associated curve,
     * the given input data may need to be sorted by x-value before being passed to the line generator.
     *
     * IMPORTANT: If the rendering context of the line generator is null,
     * then the line is returned as a path data string.
     *
     * @param data Array of data elements.
     */
    (data: Datum[]): string | null;
    /**
     * Generates a line for the given array of data. Depending on this line generator’s associated curve,
     * the given input data may need to be sorted by x-value before being passed to the line generator.
     *
     * IMPORTANT: If the line generator has been configured with a rendering context,
     * then the line is rendered to this context as a sequence of path method calls and this function returns void.
     *
     * @param data Array of data elements.
     */
    (data: Datum[]): void;

    /**
     * Returns the current x-coordinate accessor function, which defaults to a function returning first element of a two-element array of numbers.
     */
    x(): (d: Datum, index: number, data: Datum[]) => number;
    /**
     * Sets the x accessor to the specified number and returns this line generator.
     *
     * @param x A constant x-coordinate value.
     */
    x(x: number): this;
    /**
     * Sets the x accessor to the specified function and returns this line generator.
     *
     * When a line is generated, the x accessor will be invoked for each defined element in the input data array.
     *
     * The default x accessor assumes that the input data are two-element arrays of numbers. If your data are in a different format, or if you wish to transform the data before rendering,
     * then you should specify a custom accessor.
     *
     * @param x A coordinate accessor function which returns the x-coordinate value. The x accessor will be invoked for each defined element in the input data array,
     * being passed the element d, the index i, and the array data as three arguments.
    x(x: (d: Datum, index: number, data: Datum[]) => number): this;

    /**
     * Returns the current y-coordinate accessor function, which defaults to a function returning second element of a two-element array of numbers.
     */
    y(): (d: Datum, index: number, data: Datum[]) => number;
    /**
     * Sets the y accessor to the specified number and returns this line generator.
     *
     * @param y A constant y-coordinate value.
     */
    y(y: number): this;
    /**
     * Sets the y accessor to the specified function and returns this line generator.
     *
     * When a line is generated, the y accessor will be invoked for each defined element in the input data array.
     *
     * The default y accessor assumes that the input data are two-element arrays of numbers. If your data are in a different format, or if you wish to transform the data before rendering,
     * then you should specify a custom accessor.
     *
     * @param y A coordinate accessor function which returns the y-coordinate value. The y accessor will be invoked for each defined element in the input data array,
     * being passed the element d, the index i, and the array data as three arguments.
     */
    y(y: (d: Datum, index: number, data: Datum[]) => number): this;

图形加载,但线路径已填充。

这是一个掠夺者:http://plnkr.co/edit/KwcgHsc7aPpE1tjv8mlb?p=preview

问题1:

为什么我收到以下TypeScript错误:

error TS2339: Property 'x' does not exist on type '[number, number]'.

error TS2339: Property 'y' does not exist on type '[number, number]'.

问题2:

为什么我的:host(.timeline)样式没有被应用?

2 个答案:

答案 0 :(得分:3)

您需要将类型传递给d3.line

例如,如果您的模型界面名为Data

interface Data {
   x: number;
   y: number;
}

然后使用

d3.line<Data>()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y));

那应该解决问题#1

答案 1 :(得分:0)

#2的答案是我需要将角度分量的ViewEncapsulation设置为none。