Home > Software engineering >  Rendering a d3 chart in two places makes it render twice in the first area?
Rendering a d3 chart in two places makes it render twice in the first area?

Time:12-08

I am trying to display two seperate d3 pie charts on my webpage in two seperate mat-cards, but they display in the svg tag of the first d3 chart in my code.

My code:

<section class="three">
  <!-- Card 1 -->
  <div fxLayout="row" fxLayoutGap="10px">
  <mat-card class="child-card">
    <mat-card-content class="card-grid">
      <app-d3-donut class="card-grid-graph"></app-d3-donut>
    </mat-card-content>
  </mat-card>
  <!-- Card 2 -->
  <mat-card class="child-card">
    <mat-card-content class="card-grid">
      <app-apex [type]="'type1'" class="card-grid-graph"></app-apex>
    </mat-card-content>
  </mat-card>
  <!-- Card 3 -->
  <mat-card class="child-card">
    <mat-card-content class="card-grid">
      <app-d3-donut class="card-grid-graph"></app-d3-donut>
    </mat-card-content>
  </mat-card>
</div>
</section>

Both my d3 charts as I have configured in my app-d3 component display in the first card like this;

D3chart-rendering-issue

Is this because I have configured my d3 chart wrong or some other reason?

CodePudding user response:

The problem is definitely that you use d3.select('svg') or d3.select('app-d3-donut') in your custom component. d3.select is a wrapper around document.querySelector, which matches the first element it finds.

You need to make sure that this is not the first SVG element as seen from the document, but the first element as seen from the custom component. That is - you need to make sure that each Angular component can only look at and modify its own inner HTML.

From the source, d3.select also accepts HTML elements. So you should use Angular's ViewChild to get a the reference to the SVG element, and then pass that reference to d3.

@Component({
  selector: 'app-d3-donut',
  template: `
    <svg #chartEl></svg>
  `
})
class AppD3DonutComponent {
  @ViewChild('chartEl') svgElement: ElementRef;

  ngAfterViewInit() {
    d3.select(this.svgElement.nativeElement).append('g') // etc
  }
}
  • Related