【问题标题】:Using Chart.js on Angular 4在 Angular 4 上使用 Chart.js
【发布时间】:2017-09-20 17:42:03
【问题描述】:

我正在尝试将 Chart.js 与 Angular 4 一起使用,我在 chart.js 文档上看到了一个示例,但它使用 标签来拉取脚本,因此它不适用于组件。这就是我尝试融入 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>

【问题讨论】:

    标签: javascript angular typescript chart.js


    【解决方案1】:

    您可以安装该软件包以及类型,以便在诸如 Angular 等打字稿环境中获得完整功能:

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

    然后在您的组件中,您现在可以import * as Chart from 'chart.js' 并在您的打字稿环境中使用它。查看this example 了解使用打字稿的实现方法。

    因为您需要从 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: {}
        });
      }
    
    }
    

    【讨论】:

    • 我确实安装并导入了,但出现错误。请参阅更新后的问题。
    • @Gustavo 我看到了几个问题。第一个是您尝试在组件视图可用之前查询 DOM。其次是你没有得到二维上下文。我会尽快更新我的答案以解决这些问题
    • 类型包现在是@types/chart.js
    • @Ahmed chart.js 已更新其 API,不再支持该选项。我已将其从答案中删除,现在应该可以使用了。如果需要隐藏项目,我建议使用[hidden]="isHidden" 直接设置 HTML DOM 的样式。
    • @MM 它允许为您的 IDE 和 typescript 编译器键入内容,以便更轻松地编码、调试和构建项目。这不是必需的,但它确实使开发过程更加顺畅(TypeScript 与普通 JavaScript 相比,请随意使用谷歌来比较两者!)。
    【解决方案2】:

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

    在撰写此回复时,Chart.js 开发人员 have some work to do 要求导出其成员符合新标准,因此汇总可能会出现问题。

    要让 Chart.js 2.7.x 在安装包 chart.js 并在这个 angular-seed 中输入 @types/chart.js 后工作,我需要做的就是:

    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 配置。如果您使用其他东西,这可能会有所不同。

      // Add packages
      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 documentation在ngOnInit()中加载图表配置

    4. HTML 看起来像这样:

      <div class="chart-container">
         <canvas #myChart></canvas>
      </div>
      

    【讨论】:

      【解决方案3】:

      所以,经过深思熟虑,我决定加入这个线程,b/c 有一种 Angular 方法可以遵循最佳实践并且不使用任何文档范围查询......(哎呀) .为简洁起见,我在组件装饰器中使用了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 风格指南练习方法。如果您有顾虑,请将它们留在 cmets 中。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-15
      • 2017-05-05
      相关资源
      最近更新 更多