Vue中动态修改Timeline Chart步长报错:this.chart未定义
你遇到的问题核心有三点:
stepSize普通函数里的this不指向Vue实例,导致访问this.chart报错- 错误直接访问Deferred对象的
resolve属性获取Chart实例,Deferred的正确用法是通过promise链获取实例 - 直接操作DOM元素不符合Vue响应式思路,且图表更新方式错误
修复方案
1. 简化Chart实例存储,弃用Deferred
直接在Vue组件的data中声明chart变量,mounted时创建Chart实例并赋值,避免Deferred带来的复杂度。
2. 用Vue响应式数据绑定输入框
在data中添加yStepSize和xStepSize,通过v-model绑定到输入框,让数据变化自动触发响应。
3. 用watch监听步长变化,自动更新图表
通过Vue的watch属性监听步长数据,修改Chart配置后调用update()刷新图表。
修改后的完整代码
<template> <div class="chart chart-timeline"> <ChartLegend :items="legend" :allowFilterCreation="allowFilterCreation" @highlight="highlighted = $event" @toggle="toggleDatasetVisibility($event)" @apply-filter="$emit('apply-filter', $event)" v-if="legend"/> <div class="chart-container" v-show="hasData"> <canvas ref="chart"></canvas> <hr><hr><hr> Stepsize Y <!-- 用v-model绑定响应式数值 --> <input type="number" v-model.number="yStepSize"> <hr> Stepsize X <input type="number" v-model.number="xStepSize"> </div> <h2 class="subtitle is-5" v-if="!hasData"> No data </h2> </div> </template> <script> import _ from "lodash"; import Chart from "chart.js"; import Moment from "moment"; import ChartLegend from "@/components/charts/ChartLegend"; import ChartMixin from "@/components/charts/ChartMixin"; export default { props: ["data", "multiMode", "labelConverter", "valueConverter", "allowFilterCreation"], components: { ChartLegend }, mixins: [ChartMixin], data() { return { chart: null, // 直接存储Chart实例 highlighted: null, yStepSize: 1, // Y轴步长响应式数据 xStepSize: null // X轴步长响应式数据 }; }, computed: { timeline() { return this.data ? _.map(this.data.parameter, it => new Moment(it)) : []; }, keys() { return this.data ? _.map(this.data.datasets, it => it.name) : []; }, labels() { const converter = this.labelConverter || _.identity; return _.map(this.keys, converter); }, datasets() { return this.data ? _.map(this.data.datasets, it => it.data) : []; }, datasetCount() { return this.datasets.length; }, legend() { if (!this.multiMode) { return null; } return _.zipWith(this.keys, this.labels, this.palette, (key, text, color) => ({ id: key, text, color: color.normal, hidden: _.includes(this.hiddenDatasets, key) })); }, chartColors() { const highlightIndex = this.highlighted !== null && !_.includes(this.hiddenDatasets, this.highlighted) ? _.indexOf(this.keys, this.highlighted) : null; const colors = []; for (const index of _.keys(this.datasets)) { const color = this.palette[index]; if (highlightIndex !== null) { if (index == highlightIndex) { colors.push({ backgroundColor: color.strongMuted, borderColor: color.strong }); } else { colors.push({ backgroundColor: color.weakMuted, borderColor: color.weak }); } } else { colors.push({ backgroundColor: color.normalMuted, borderColor: color.normal }); } } return colors; } }, mounted() { // 创建Chart实例并赋值给data变量 this.chart = new Chart(this.$refs.chart, { type: "line", data: { datasets: [], labels: [] }, options: { maintainAspectRatio: false, legend: { display: false }, scales: { xAxes: [{ type: "time", time: { isoWeekday: true, displayFormats: { hour: "HH:mm", day: "DD-MM", week: "DD-MM", month: "MM-YYYY" } }, ticks: { source: "labels" } }], yAxes: [{ ticks: { min: 0, precision: 0, stepSize: this.yStepSize } }] }, spanGaps: false, tooltips: { callbacks: { label: item => { const converter = this.valueConverter || _.identity; const label = this.multiMode ? (this.labels[item.datasetIndex] + ": ") : ""; return label + converter(this.datasets[item.datasetIndex][item.index]); } } } } }); }, watch: { data() { this.resetDatasetVisibility(); }, // 监听Y轴步长变化,更新图表配置 yStepSize(newVal) { if (this.chart) { this.chart.options.scales.yAxes[0].ticks.stepSize = newVal; this.chart.update(); } }, // 监听X轴步长变化,更新图表配置 xStepSize(newVal) { if (this.chart) { this.chart.options.scales.xAxes[0].ticks.stepSize = newVal; this.chart.update(); } } } } </script>
关键修改说明
- 移除Deferred,直接存储Chart实例,避免异步获取实例的错误
- 输入框用
v-model.number绑定,自动处理数值类型,无需手动查询DOM - 通过
watch监听步长数据变化,自动触发图表更新,符合Vue响应式逻辑 - 更新图表时直接修改
chart.options配置,调用chart.update()是Chart.js官方推荐的刷新方式
内容的提问来源于stack exchange,提问作者GTmember
相关产品推荐
相关产品推荐

