开发者问题收集

在 Angular 4 上使用 Chart.js

2017-09-20
32851

我尝试将 Chart.js 与 Angular 4 结合使用,我在 chart.js 文档中看到了一个示例,但它使用 < script > 标记来提取脚本,因此它无法在组件上运行。这是我尝试将其融入 Angular 的方式:

TS

export class GraphicsComponent implements OnInit {

  ctx = document.getElementById("myChart");
  myChart = new Chart(this.ctx, {
    type: 'pie',
    data: {
        labels: ["New", "In Progress", "On Hold"],
        datasets: [{
            label: '# of Votes',
            data: [1,2,3],
            backgroundColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
      responsive: false,
      display:true
    }
  });

  constructor() { }

  ngOnInit() {
  }

}

HTML

<canvas id="myChart" width="700" height="400"></canvas>

您知道如何让它运行吗?

编辑: 我已使用导入更新了我的代码,但现在收到错误。

导入代码:

import * as Chart from 'chart.js'

chrome 控制台上的错误:

ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'length' of null
TypeError: Cannot read property 'length' of null
    at Object.acquireContext (platform.dom.js:333)
    at Chart.construct (core.controller.js:74)
    at new Chart (core.js:42)
    at new GraphicsComponent (graphics.component.ts:12)
    at createClass (core.es5.js:10922)
    at createDirectiveInstance (core.es5.js:10756)
    at createViewNodes (core.es5.js:12197)
    at createRootView (core.es5.js:12092)
    at callWithDebugContext (core.es5.js:13475)
    at Object.debugCreateRootView [as createRootView] (core.es5.js:12792)
    at Object.acquireContext (platform.dom.js:333)
    at Chart.construct (core.controller.js:74)
    at new Chart (core.js:42)
    at new GraphicsComponent (graphics.component.ts:12)
    at createClass (core.es5.js:10922)
    at createDirectiveInstance (core.es5.js:10756)
    at createViewNodes (core.es5.js:12197)
    at createRootView (core.es5.js:12092)
    at callWithDebugContext (core.es5.js:13475)
    at Object.debugCreateRootView [as createRootView] (core.es5.js:12792)
    at resolvePromise (zone.js:795)
    at resolvePromise (zone.js:766)
    at zone.js:844
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:425)
    at Object.onInvokeTask (core.es5.js:3881)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask (zone.js:192)
    at drainMicroTaskQueue (zone.js:602)
    at <anonymous>
3个回答

您可以在 Typescript 环境(例如 Angular)中安装该软件包以及类型,以实现完整功能:

npm install --save chart.js && npm install --save-dev @types/chart.js

然后,您现在可以在组件中 import * as Chart from 'chart.js' 并在 Typescript 环境中使用它。查看 此示例 ,了解使用 Typescript 的实现方法。

由于您需要从 DOM 获取画布,因此您需要确保在尝试访问它之前已渲染它。这可以通过 AfterViewInit 来实现。

import { Component, AfterViewInit } from '@angular/core';
import * as Chart from 'chart.js'

export class MyChartComponent implements AfterViewInit {

  canvas: any;
  ctx: any;

  ngAfterViewInit() {
    this.canvas = document.getElementById('myChart');
    this.ctx = this.canvas.getContext('2d');
    let myChart = new Chart(this.ctx, {
      type: 'pie',
      data: {
          labels: ["New", "In Progress", "On Hold"],
          datasets: [{
              label: '# of Votes',
              data: [1,2,3],
              backgroundColor: [
                  'rgba(255, 99, 132, 1)',
                  'rgba(54, 162, 235, 1)',
                  'rgba(255, 206, 86, 1)'
              ],
              borderWidth: 1
          }]
      },
      options: {}
    });
  }

}
Z. Bagley
2017-09-20

在我的 Angular 6.1 网站中,我在 mgechev/angular-seed 中单独使用 chart.js。

在撰写此回复时,Chart.js 开发人员 还有一些工作要做 ,以使其成员的导出符合较新的标准,因此汇总可能会出现问题。

要在安装此 angular-seed 中的包 chart.js 和类型 @types/chart.js 后使 Chart.js 2.7.x 正常工作,我需要做的就是:

  1. 更新 project.config.ts 以包含ROLLUP_NAMED_EXPORTS 使汇总正常工作(如果您使用汇总)。

    this.ROLLUP_NAMED_EXPORTS = [
    ...this.ROLLUP_NAMED_EXPORTS,
    { 'node_modules/chart.js/src/chart.js': ['Chart'] }
    ];
    
  2. 更新 project.config.ts 以包含其他包。此种子使用 SystemJS 配置。如果您使用其他配置,这可能会有所不同。

    // 添加包
    const additionalPackages: ExtendPackages[] = [
    { name: 'chart.js', path: 'node_modules/chart.js/dist/Chart.bundle.min.js' },
    
    ...
    
    ];
    
  3. 在您的组件中

    import { Chart } from 'chart.js';
    
    ...
    
    export class MyComponent implements OnInit {
    
    @ViewChild('myChart') myChartRef: ElementRef;
    chartObj: Chart;
    
    ...
    }
    

    然后根据 Chart.js 文档

  4. 在 ngOnInit() 中加载图表配置。
    <div class="chart-container">
    <canvas #myChart></canvas>
    </div>
    
Dave Skender
2018-10-03

因此,经过深思熟虑后,我决定加入这个主题,因为有一种 Angular 方式可以做到这一点,它遵循最佳实践并且不使用任何文档范围查询......(呸)。为了简洁起见,我在 Component 装饰器中使用了 template ,而不是 templateUrl ,但它们的行为与在模板中相同。DOM 仍然必须先加载,这是 Chart.js 的一大陷阱。此示例适用于 Angular 8+,适用于我当前的版本 9.1.7

import { Component, ViewChild, AfterViewInit, Input } from '@angular/core'
import { Chart } from 'chart.js'

@Component({
  selector: 'app-chart',
  template: `<canvas #myChart></canvas>,
})
export class SkillsChartComponent implements AfterViewInit {
  @ViewChild('myChart', {
    static: true
  }) myChart: {
    nativeElement: HTMLCanvasElement
  }

  constructor() {}

  ngAfterViewInit() {
    this.createChart()
  }

  private createChart(): void {
    const elem = this.myChart.nativeElement
    try {
      new Chart(elem, {
        type: 'pie',
        data: {
          labels: ["New", "In Progress", "On Hold"],
          datasets: [{
              label: '# of Votes',
              data: [1,2,3],
              backgroundColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)'
              ],
              borderWidth: 1
            }]
          },
          options: {
          responsive: false,
          display:true
        }
      })
    } catch (err) {
      throw new Error(err) // You can either throw an error or do whatever here...
    } finally {
      // Any cleanup of logging...
    }
  }

}

这里发生的事情是,您获得了对 Canvas 本身的引用,并使用 Angular 的生命周期钩子来表示一旦 HTML 内容初始化,就使用 Chart 绘制到画布上。事实上,Chart 的第一个参数是元素本身,这会导致很多反模式,但您绝对不需要使用 vanilla JS 查询文档范围来获取特定画布的句柄,这是根据 Angular 样式指南执行此操作的最佳实践方式。如果您有疑问,请在评论中留言。

Joshua Michael Calafell
2020-06-18