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

如何在ASP.NET MVC中用Chart.js实现动态数据集及多问题饼图展示

解决多问题饼图仅第一个显示的坑

兄弟我太懂你这个问题了!之前用MVC做类似的投票统计页面时,也踩过ViewBag的这个坑——你现在应该是把单个饼图数据塞到ViewBag里,循环渲染问题的时候,后面的问题会覆盖ViewBag里的旧数据,结果只有第一个问题能拿到正确的图表数据,后面的全空了。

核心问题就是:ViewBag是全局的动态对象,不能用来存多个独立的数据集,你得给每个问题绑定专属的图表数据才行。下面给你两种靠谱的实现方式,推荐第一种,代码更规范:

1. 用ViewModel打包每个问题+图表数据(最推荐)

先搞个ViewModel类,把每个问题的基本信息和对应的饼图数据打包在一起,这样每个问题都有自己独立的“数据包”,不会互相覆盖:

public class QuestionChartVM
{
    // 问题ID,用来给饼图容器生成唯一ID
    public int QuestionId { get; set; }
    // 问题内容
    public string QuestionText { get; set; }
    // 饼图需要的键值对(比如选项名称+投票数)
    public Dictionary<string, int> PieChartData { get; set; }
}

控制器里组装数据

在控制器中,把数据库里的每个问题和它对应的饼图数据都塞进ViewModel列表,然后传给视图:

public ActionResult QuestionStats()
{
    var questionChartList = new List<QuestionChartVM>();
    
    // 从数据库拉所有问题
    var allQuestions = _db.Questions.ToList();
    
    foreach (var q in allQuestions)
    {
        // 拉当前问题对应的统计数据(比如每个选项的投票数)
        var chartData = _db.UserAnswers
            .Where(a => a.QuestionId == q.Id)
            .GroupBy(a => a.SelectedOption)
            .ToDictionary(g => g.Key, g => g.Count());
        
        // 把问题和图表数据打包进VM
        questionChartList.Add(new QuestionChartVM
        {
            QuestionId = q.Id,
            QuestionText = q.Content,
            PieChartData = chartData
        });
    }
    
    // 用强类型视图传递,比ViewBag更安全
    return View(questionChartList);
}

视图里循环渲染

先在视图顶部指定强类型模型:

@model List<QuestionChartVM>

然后循环每个ViewModel,给每个饼图生成唯一的容器ID,再用当前VM里的专属数据初始化饼图(这里以Chart.js为例,其他图表库逻辑一样):

@foreach (var item in Model)
{
    <div class="question-card">
        <h4>@item.QuestionText</h4>
        <!-- 每个饼图用QuestionId做后缀,保证ID唯一 -->
        <canvas id="pieChart-@item.QuestionId" width="300" height="300"></canvas>
    </div>

    <script>
        // 针对当前问题的饼图初始化
        const ctx = document.getElementById('pieChart-@item.QuestionId').getContext('2d');
        new Chart(ctx, {
            type: 'pie',
            data: {
                labels: @Html.Raw(Json.Serialize(item.PieChartData.Keys)),
                datasets: [{
                    data: @Html.Raw(Json.Serialize(item.PieChartData.Values)),
                    backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF']
                }]
            },
            options: {
                responsive: true
            }
        });
    </script>
}

2. 备选:用ViewBag传字典(快速临时方案)

如果不想新建ViewModel,也可以用ViewBag传递一个以QuestionId为键的字典,把每个问题的图表数据存进去:

// 控制器里
ViewBag.QuestionCharts = allQuestions.ToDictionary(q => q.Id, q => 
    _db.UserAnswers.Where(a => a.QuestionId == q.Id)
                  .GroupBy(a => a.SelectedOption)
                  .ToDictionary(g => g.Key, g => g.Count())
);
// 同时把问题列表也传给ViewBag
ViewBag.AllQuestions = allQuestions;

视图里循环渲染:

@foreach (var q in ViewBag.AllQuestions)
{
    <div>
        <h4>@q.Content</h4>
        <canvas id="pieChart-@q.Id"></canvas>
        <script>
            // 从字典里取当前问题的图表数据
            const chartData = @Html.Raw(Json.Serialize(ViewBag.QuestionCharts[q.Id]));
            const ctx = document.getElementById('pieChart-@q.Id').getContext('2d');
            new Chart(ctx, {
                type: 'pie',
                data: {
                    labels: Object.keys(chartData),
                    datasets: [{ data: Object.values(chartData) }]
                }
            });
        </script>
    </div>
}

为啥原来的方式不行?

简单说就是ViewBag是“全局变量”,你每次给ViewBag.ChartData赋值,都会把之前的值冲掉。当视图循环到第二个问题时,ViewBag里的ChartData已经是最后一个问题的数据了,但第一个问题已经渲染完了,后面的问题拿到的都是错误/重复的数据,自然显示不出来。

内容的提问来源于stack exchange,提问作者mj.e

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:40:10