Angular集成Chartist首次加载报querySelectorAll为null 刷新后图表正常
错误原因
报错TypeError: Cannot read property 'querySelectorAll' of null是因为你在ngOnInit生命周期钩子中执行Chartist图表初始化,该阶段组件模板的DOM还未完成渲染挂载,通过类选择器.ct-chart-line无法匹配到对应元素,Chartist内部拿到空值后调用querySelectorAll触发报错。刷新后正常是因为浏览器缓存加快了DOM渲染速度,属于偶发正常,并非逻辑本身没问题。
解决方案
按照以下步骤修改即可:
- 调整生命周期钩子,将初始化逻辑移到DOM渲染完成后触发的
ngAfterViewInit钩子中 - 使用
@ViewChild获取组件内部DOM元素,替换全局类选择器,避免多实例冲突 - 兼容异步输入数据的场景,确保数据和DOM都准备完成后再初始化图表
具体代码修改
1. 修改 line.component.ts
// 新增导入AfterViewInit、ViewChild、ElementRef import { Component, OnInit, Input, ViewEncapsulation, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; import { ChartData } from '../chart.data.namespace'; import * as Chartist from 'chartist'; import { NChartist } from '../NChartist.namespace'; import { IBarChartOptions, IChartistAnimationOptions, IChartistData } from 'chartist'; @Component({ selector: 'app-line', templateUrl: './line.component.html', styleUrls: ['./line.component.css'], encapsulation:ViewEncapsulation.None, }) // 实现AfterViewInit接口 export class LineComponent implements OnInit, AfterViewInit { chartline: any; // 用setter监听输入数据的变化,兼容异步传值 @Input() set data(value: ChartData.LabelValue[]) { this._data = value; this.tryInitChart(); } private _data: ChartData.LabelValue[]; // 获取模板中的DOM元素 @ViewChild('chartLineEl') chartLineEl: ElementRef; constructor() { } ngOnInit() { // 移除原有初始化逻辑 } ngAfterViewInit() { // DOM渲染完成后尝试初始化图表 this.tryInitChart(); } // 统一判断初始化条件,避免重复代码 private tryInitChart() { if (this.chartLineEl?.nativeElement && this._data?.length) { this.createLineChart(); } } createLineChart() { let linedata: NChartist.Data = new NChartist.Data(); this.prepareLineData(linedata); const prepared_l_data = { series: [linedata.series], labels: linedata.labels, } // 传入获取到的DOM元素,代替类选择器 this.chartline = new Chartist.Line(this.chartLineEl.nativeElement, prepared_l_data,{}) console.log(this.chartline); this.chartline.update(); this.startAnimationForLineChart(this.chartline); } prepareLineData(linedata) { for (let i = 0; i < this._data.length; i++) { linedata.series[i] = this._data[i].value; linedata.labels[i] = this._data[i].label; } } startAnimationForLineChart(chart){ let seq: any, delays: any, durations: any; seq = 0; delays = 80; durations = 500; chart.on('draw', function(data) { if(data.type === 'line' || data.type === 'area') { data.element.animate({ d: { begin: 600, dur: 700, from: data.path.clone().scale(1, 0).translate(0, data.chartRect.height()).stringify(), to: data.path.clone().stringify(), easing: Chartist.Svg.Easing.easeOutQuint } }); } else if(data.type === 'point') { seq++; data.element.animate({ opacity: { begin: seq * delays, dur: durations, from: 0, to: 1, easing: 'ease' } }); } }); seq = 0; }; }
2. 修改 line.component.html
给图表容器增加模板引用变量#chartLineEl:
<body> <div class="card card-chart"> <div class="card-header card-header-warning"> <!-- 新增#chartLineEl --> <div class="ct-chart-line" id="chartline" #chartLineEl></div> </div> <div class="card-body"> <h4 class="card-title">Line Chart</h4> <p class="card-category"> <span class="text-success"><i class="fa fa-long-arrow-up"></i> 55% </span> increase in today sales. </p> </div> <div class="card-footer"> <div class="stats"> <i class="material-icons">access_time</i> updated 4 minutes ago </div> </div> </div> </body>
内容的提问来源于stack exchange,提问作者dilemunal
相关产品推荐
相关产品推荐

