尝试将两个 d3 折线图放在一个 Ionic2 页面中



我正在尝试在Ionic2应用程序内的一页上有两个(或更多(相似的图表。我使用 d3-ng2-service 来包装 Angular2 的 d3 类型。我的问题是:当我尝试将两个图形分别放置在两个不同的div 元素中时,两个元素的绘制都失败了。当我选择页面中的第一个div 时,第二个图表覆盖了第一个图表,但它确实被绘制了。

有没有一种聪明的方法可以放置图表而不是一个图表?这些示例总是为外部容器提供一个唯一的 id,这也是我尝试做的事情:

import { Component, Input, OnInit, ElementRef } from '@angular/core';
import { D3Service, D3, Selection, ScaleLinear, ScaleTime, Axis, Line } from 'd3-ng2-service'; // <-- import the D3 Service, the type alias for the d3 variable and the Selection interface

@Component({
  selector: 'd3-test-app',
  templateUrl: 'd3-test-app.html',
  providers: [D3Service],
})
export class D3TestAppComponent {
  //Time is on x-axis, value is on y-axis
  @Input('timeSeries') timeSeries: Array<{isoDate: string | Date | number | {valueOf(): number}, value: number}>;
  @Input('ref') ref: string;
  /* the size input defines, how the component is drawn  */
  @Input('size') size: string;

  private d3: D3;
  private margin: {top: number, right: number, bottom: number, left: number};
  private width: number;
  private height: number;
  private d3ParentElement: Selection<any, any, any, any>; // <-- Use the Selection interface (very basic here for illustration only)

  constructor(element: ElementRef,
              d3Service: D3Service) { // <-- pass the D3 Service into the constructor
              this.d3 = d3Service.getD3(); // <-- obtain the d3 object from the D3 Service
              this.d3ParentElement = element.nativeElement;
  }
  ngOnInit() {
    let x: ScaleTime<number, number>;
    let y: ScaleLinear<number, number>;
    let minDate: number;
    let maxDate: number;
    let minValue: number = 0;
    let maxValue: number;
    // set the dimensions and margins of the graph
    switch (this.size) {
      case "large":
        this.margin = {top: 20, right: 20, bottom: 30, left: 50};
        this.width = 640 - this.margin.left - this.margin.right;
        this.height = 480 - this.margin.top - this.margin.bottom;
        break;
      case "medium":
          this.margin = {top: 20, right: 0, bottom: 20, left: 20};
          //golden ratio
          this.width = 420 - this.margin.left - this.margin.right;
          this.height = 260 - this.margin.top - this.margin.bottom;
          break;
      case "small":
        this.margin = {top: 2, right: 2, bottom: 3, left: 5};
        this.width = 120 - this.margin.left - this.margin.right;
        this.height = 80 - this.margin.top - this.margin.bottom;
        break;
      default:
        this.margin = {top: 20, right: 20, bottom: 30, left: 50};
        this.width = 640 - this.margin.left - this.margin.right;
        this.height = 480 - this.margin.top - this.margin.bottom;
    }

    // ...
    if (this.d3ParentElement !== null) {
      let d3 = this.d3; // <-- for convenience use a block scope variable
      //THIS FAILS...   
      let selector: string = '#' + this.ref + ' .graphContainer';
      console.log(selector);
      let svg = d3.select( selector).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 + ")");

          this.timeSeries.forEach((d) => {
            d.isoDate = +d3.isoParse(d.isoDate as string);
            d.value = +d.value;
            if (minDate == null || minDate >= d.isoDate) {
              minDate = d.isoDate as number;
            }
            if (maxDate == null || maxDate <= d.isoDate) {
              maxDate = d.isoDate as number;
            }
            // if (minValue == null || minValue >= d.value) {
            //   minValue = d.value as number;
            // }
            if (maxValue == null || maxValue <= d.value) {
              maxValue = d.value as number;
            }
          });
      // TODO magic numbers to real min max
      x = d3.scaleTime().domain([minDate, maxDate]).range([0,this.width]);
      y = d3.scaleLinear().domain([0, maxValue]).range([this.height, 0]);
      let xAxis: Axis<number | Date | {valueOf() : number;}> = d3.axisBottom(x);
      let yAxis: Axis<number | {valueOf(): number;}> = d3.axisLeft(y);
      let valueLine: Line<{isoDate: number; value: number}> = d3.line<{ isoDate: number; value: number }>()
       .x(function (d) { return x(d.isoDate)})
       .y(function (d) { return y(d.value)});

      // Add the valueline path.
      svg.append("path")
        .data([this.timeSeries as {isoDate: number, value: number}[]])
        .attr("class", "line")
        .attr("d", valueLine);
      svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + this.height + ")")
        .call(xAxis)
      svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
    }
  }
  myParser() : (string) => Date {
    return this.d3.utcParse("%Y-%m-%dT%H:%M:%S.%LZ");
  }
}

该 HTML:

<div class='graphContainer'>
</div>

使用自定义组件的 HTML 文件:

<ion-header>
  <ion-navbar #dashboardNav>
    <ion-title>Dashboard</ion-title>
    <button ion-button menuToggle="favMenu" right>
        <ion-icon name="menu"></ion-icon>
    </button>
  </ion-navbar>
</ion-header>
<ion-content>
  <ion-item *ngFor="let entry of dashboard">
    {{ entry.name }}
    <d3-test-app [id]='entry.name' [timeSeries]='entry.timeSeries' [ref]='entry.name' size='medium'></d3-test-app>
  </ion-item>
</ion-content>

很难在没有看到堆栈跟踪的情况下进行调试,但由于您选择元素的方式,这看起来失败了。我的回答将基于这一假设。

当您必须在 DOM 内部查找特定元素并且确定只有一个元素具有该 ID 时,按 ID 查询很方便。由于您在 Angular 元素中,因此您已经拥有了所需的引用,它是元素本身,无需动态创建 ID 引用。

我根本不是 ng2 的专家,但看看如何在 Angular 中选择原始元素并为框架选择最佳方法。假设你选择类似这个答案上显示的例子:

  constructor(public element: ElementRef) {
    this.element.nativeElement // <- your direct element reference 
  }

注意 - 看起来有多种方法可以实现这一点......不确定这是最好/正确的,但无论如何目标都是获得参考

然后只需通过 D3 dsl 选择它,就像您已经通过传递原始引用所做的那样

 // At this point this.element.nativeElement
 // should contain the raw element reference
    if (this.d3ParentElement !== null) {
      const { d3, element } = this; // <-- for convenience use block scope variables
      const svg = d3.select(element.nativeElement).append("svg")...

相关内容

  • 没有找到相关文章

最新更新