2

I would like to create a link to allow the user to download the displayed graph. The way i am currently trying to get it to work is with .toDataUrl is that considered a safe way or is there another way of going about doing this.

HTML:

<canvas id="myChart" baseChart [colors]="colorsOverride" [datasets]="barChartData" [labels]="barChartLabels" [options]="barChartOptions" [legend]="barChartLegend"
    [chartType]="barChartType" (chartHover)="chartHovered($event)" (chartClick)="chartClicked($event)">
</canvas>

<div class="footer">
  <button (click)="exportGraph()">Export Graph</button>
</div>

Components:

  export_graph = <HTMLCanvasElement>document.getElementById("myChart");
  downloadLink: string;

  exportGraph(){
    this.downloadLink = this.export_graph.toDataURL("image/png");
  }

When i try to export this is the error message i get in my console: Cannot read property 'toDataURL' of null

bluePearl
  • 1,237
  • 4
  • 24
  • 42
  • at the point where the line `export_graph = document.getElementById("myChart");` is executed, the dom for your component is not constructed. Thus, it will return null. try putting it in an `ngAfterViewInit` and dont forget to implement `AfterViewInit`.. working on a plunker and an answer. – Ahmed Musallam May 02 '17 at 21:29
  • Possible duplicate of [Why does jQuery or a DOM method such as getElementById not find the element?](http://stackoverflow.com/questions/14028959/why-does-jquery-or-a-dom-method-such-as-getelementbyid-not-find-the-element) – Heretic Monkey May 02 '17 at 21:45
  • @MikeMcCaughan well - i am using typescript along with angular2 and my question was more towards using ng-charts (charts.js) and none of the suggested solutions are applicable for my code structure. – bluePearl May 02 '17 at 21:49
  • TypeScript compiles to JavaScript, so anything you do in JavaScript can be used in TypeScript. For Angular, just use [`ngOnInit`](https://angular.io/docs/ts/latest/api/core/index/OnInit-interface.html). – Heretic Monkey May 02 '17 at 21:55

1 Answers1

11

You should use an anchor tag <a> instead of <button>, you can style it to look just like a button. Then you can attach a click event and do it this way:

plunker: http://plnkr.co/edit/xyfWok58R3eQdYk7pAds?p=preview

first, add the download link to your html <a href="#" (click)="downloadCanvas($event)"> DOWNLOAD THIS</a>

then create the downloadCanvas function

downloadCanvas(event) {
    // get the `<a>` element from click event
    var anchor = event.target;
    // get the canvas, I'm getting it by tag name, you can do by id
    // and set the href of the anchor to the canvas dataUrl
    anchor.href = document.getElementsByTagName('canvas')[0].toDataURL();
    // set the anchors 'download' attibute (name of the file to be downloaded)
    anchor.download = "test.png";
}

it is important to do the document.getElement... on click instead of before-hand. This way you know for sure the html view and <canvas> has rendered and is done drawing (you see it on the page).

the way you are doing it in your question, you are looking for <canvas> element before it's even rendered on the page, that why it's undefined.

Ahmed Musallam
  • 9,523
  • 4
  • 27
  • 47
  • Thanks this is working as expected now. But one questions when i am doing the export is there a way to control the size of the exported graph? on display it is a small suitable size but when exporting i would like to increase its size. – bluePearl May 02 '17 at 23:02
  • you'll have to recreate the `canvas` with the large size you're looking for then download it as per my answer. You can keep the canvas hidden while you do that. I say this because if you just enlarge a small canvas image, it will look pixilated. – Ahmed Musallam May 02 '17 at 23:21
  • true - thanks i will go ahead and give that a try. Also i was further testing this solution out and realized that if i have multiple graphs on the page and regardless which of the graphs i click to download only one graph type is downloaded - Will i need to pass in an id or some distinguisher as to which graph? – bluePearl May 02 '17 at 23:37
  • You can find the canvas relative to your button location. maybe the button and canvas share the same parent and you can find the specific canvas that way. alternatively, look into writing a plugin for chartjs. – Ahmed Musallam May 02 '17 at 23:40