You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue中提交表单时如何动态向Chart.js折线图添加数据?

问题:Vue中动态更新Chart.js折线图不生效

我在Vue项目里尝试给Chart.js折线图动态添加数据,期望用户提交表单后图表能自动更新显示新数据。目前已经能将新数据push到App.vue的data中的datasets数组,但图表不会渲染新数据;手动在datasets数组中添加数据却能正常显示,请问该如何解决?

相关代码

App.vue

<template>
<div>
  <form>
     <div class="row">
      <div class="mb-3 col-6">
          <label class="form-label">Score</label>
          <input type="number" min="0" max="100" class="form-control" id="score"
              name="score" placeholder="Score in %" v-model='score' />
      </div>
      <div class="mb-3 form-check col-6">
          <label class="form-label">Exam Type</label>
          <select class="form-select form-select"
              aria-label=".form-select-sm example" id="examType"
              v-model='examType'>
              <option value="CA1">CA1</option>
              <option value="SA1">SA1</option>
              <option value="CA2">CA2</option>
              <option value="SA2">SA2</option>
          </select>
      </div>
  </div>
  <div class="row">
      <div class="mb-3">
          <label class="form-label">Subject</label>
          <input type="text" class="form-control" id="subject" name="subject"
              placeholder="" v-model='subject' />
      </div>
  </div>

   <div class="modal-footer d-block">
      <button type="submit" data-bs-dismiss="modal"
          class="btn btn-warning float-end" @click="addResult">Submit</button>
  </div>
 </form>
<div>
 <ChartTest :data="data" :title='title' />
</div>
</div>
</template>

<script>
    import ChartTest from "../components/ProgressPage/ChartTest.vue";
    export default {
        name: "Progress",
        components: {
            ChartTest
    },
        
        data() {
            return {
                score: '',
                examType: '',
                subject: '',
                existingSubjects: [],
                colors: ["#3e95cd", "#8e5ea2", "#3cba9f", "#e8c3b9", "#c45850"],
                title: '',
                data: {
                    labels: ['CA1', 'SA1', 'CA2', 'SA2'],
                    datasets: []
                },

            }
        },
    methods: {
            addResult() {
             
                let count = this.existingSubjects.length
             
                if (!this.existingSubjects.includes(this.subject)) {
                    this.existingSubjects.push(this.subject)
                    console.log(this.subject)
                    const newData = {
                        data: [this.score,54,43],
                        label: this.subject,
                        borderColor: this.colors[count],
                        fill: false
                    }
                    this.data.datasets.push(newData)
                    console.log( this.data.datasets)

                } else {
                    //TBC
                }
             

            }
        },
    }
</script>

ChartTest.vue

<template>
    <canvas id="progress-chart" width="600" height="450"></canvas>
</template>

<script>
    import Chart from 'chart.js/auto';
    export default {
        name: 'ChartTest',
        props: {
            data: Object,
            title: String

        },
        mounted() {
            new Chart(document.getElementById("progress-chart"), {
                type: 'line',
                data: this.data,
                options: {
                    plugins: {
                        title: {
                            display: true,
                            text: this.title
                        }
                    },
                    scales: {

                        y: {
                            display: true,
                            stacked: true,
                            max: 0,
                            min: 100,
                            title: {
                                display: true,
                                text: 'Your Score (%)'
                            }
                        }
                    }
                }
            });
        }
    }
</script>

解决方案

1. 核心问题:Chart.js实例未监听数据变化

你的ChartTest组件只在mounted钩子中初始化了一次图表,之后即使父组件的data prop更新,Chart.js实例也不会自动感知到数据变化,必须手动触发更新。

2. 修改ChartTest组件,实现动态更新

修改ChartTest.vue,保存图表实例并监听props变化:

<template>
    <canvas id="progress-chart" width="600" height="450"></canvas>
</template>

<script>
import Chart from 'chart.js/auto';
export default {
    name: 'ChartTest',
    props: {
        data: Object,
        title: String
    },
    data() {
        return {
            chartInstance: null // 保存图表实例
        }
    },
    mounted() {
        this.initChart();
    },
    methods: {
        // 初始化图表
        initChart() {
            this.chartInstance = new Chart(document.getElementById("progress-chart"), {
                type: 'line',
                data: this.data,
                options: {
                    plugins: {
                        title: {
                            display: true,
                            text: this.title
                        }
                    },
                    scales: {
                        y: {
                            display: true,
                            stacked: false, // 折线图无需堆叠,原配置有误
                            max: 100, // 原配置max/min写反,纠正为max=100,min=0
                            min: 0,
                            title: {
                                display: true,
                                text: 'Your Score (%)'
                            }
                        }
                    }
                }
            });
        },
        // 更新图表
        updateChart() {
            if (this.chartInstance) {
                this.chartInstance.data = this.data;
                this.chartInstance.update(); // 手动触发Chart.js更新
            }
        }
    },
    watch: {
        // 深度监听data对象的变化
        data: {
            handler() {
                this.updateChart();
            },
            deep: true
        },
        // 监听title变化
        title(newTitle) {
            if (this.chartInstance) {
                this.chartInstance.options.plugins.title.text = newTitle;
                this.chartInstance.update();
            }
        }
    }
}
</script>

3. 修复App.vue中的数据逻辑问题

原代码中新增的dataset数据长度(3个)与labels长度(4个)不匹配,且未根据选中的examType对应赋值,同时补充已有学科的更新逻辑:

methods: {
    addResult() {
        // 简单校验,避免空数据提交
        if (!this.subject || this.score === '') return;

        const examIndex = this.data.labels.indexOf(this.examType);
        let count = this.existingSubjects.length;

        if (!this.existingSubjects.includes(this.subject)) {
            this.existingSubjects.push(this.subject);
            // 初始化与labels长度一致的数组,仅给选中的考试类型赋值
            const initialData = new Array(this.data.labels.length).fill(null);
            initialData[examIndex] = Number(this.score); // 转数字,避免字符串类型导致图表异常

            const newData = {
                data: initialData,
                label: this.subject,
                borderColor: this.colors[count % this.colors.length], // 取模避免颜色数组越界
                fill: false
            };
            this.data.datasets.push(newData);
        } else {
            // 更新已有学科的对应考试分数
            const targetDataset = this.data.datasets.find(item => item.label === this.subject);
            if (targetDataset) {
                targetDataset.data[examIndex] = Number(this.score);
            }
        }

        // 提交后清空表单
        this.score = '';
        this.examType = '';
        this.subject = '';
    }
}

4. 关键说明

  • Vue的响应式系统能检测到this.data.datasets.push()的变化,但Chart.js无法自动感知,必须通过chartInstance.update()手动触发渲染。
  • 确保dataset的data数组长度与labels一致,否则图表会显示异常。
  • 原Chart配置中y轴的max和min写反,导致图表无法正常显示分数,已纠正。

内容的提问来源于stack exchange,提问作者user121443

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 19:50:23