Search code examples
javascriptc#asp.netasp.net-mvcchart.js

Pie Chart is not populating in ASP.NET MVC page


I am having trouble populating my pie chart using Chart.js. The chart legend comes up but the values are undefined.

cshtml

@page
@model FinalProject.Pages.Exam.ReportModel
@{
    Layout = null;
}

<h2>Letter Grade Report</h2>
<table>
    <tr>
        <th>Exam ID</th>
        <th>Student ID</th>
        <th>Score</th>
        <th>Letter Grade</th>
    </tr>
    @foreach (var exam in Model.Exams)
    {
        <tr>
            <td>@exam.ExamId</td>
            <td>@exam.StudentID</td>
            <td>@exam.Score</td>
            <td>@FinalProject.Pages.Exam.ReportModel.CalculateLetterGrade(exam.Score)</td>
        </tr>
    }
</table>

<h2>Letter Grade Summary</h2>
<table>
    <tr>
        <th>Letter Grade</th>
        <th>Total Count</th>
    </tr>
    @foreach (var gradeSummary in Model.GradeSummary)
    {
        <tr>
            <td>@gradeSummary.LetterGrade</td>
            <td>@gradeSummary.TotalCount</td>
        </tr>
    }
</table>

<!-- Add Chart.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.min.js"></script>

<h2>Letter Grade Distribution (Pie Chart)</h2>
<div style="max-height:300px;">
    <canvas id="gradeChart"></canvas>
</div>

<script>
    let gradeData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.GradeSummary));

    const data = {
        labels: gradeData.map(g => g.letterGrade),
        datasets: [{
            label: 'Grade Distribution',
            data: gradeData.map(g => g.totalCount),
            backgroundColor: ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'],
            hoverOffset: 4
        }]
    };

    const config = {
        type: 'pie',
        data: data,
        options: {
            responsive: true,
            maintainAspectRatio: false
        }
    };

    new Chart(document.getElementById('gradeChart'), config);
</script>

cshtml.cs

using Microsoft.AspNetCore.Mvc.RazorPages;
using School.DAL;
using System;
using System.Collections.Generic;
using System.Linq;

namespace FinalProject.Pages.Exam
{
    public class ReportModel : PageModel
    {
        private readonly IExamAdapter _examAdapter;

        public ReportModel(IExamAdapter examAdapter)
        {
            _examAdapter = examAdapter;
        }

        public List<School.DAL.Exam> Exams { get; set; }
        public List<GradeSummary> GradeSummary { get; set; }

        public void OnGet()
        {
            Exams = _examAdapter.GetAll().ToList();

            // Calculate letter grades and generate summary
            GradeSummary = CalculateGradeSummary(Exams);
        }

        private List<GradeSummary> CalculateGradeSummary(List<School.DAL.Exam> exams)
        {
            var gradeSummary = new List<GradeSummary>();

            var gradeGroups = exams.GroupBy(e => CalculateLetterGrade(e.Score));

            foreach (var group in gradeGroups)
            {
                gradeSummary.Add(new GradeSummary
                {
                    LetterGrade = group.Key,
                    TotalCount = group.Count()
                });
            }

            return gradeSummary;
        }

        public static string CalculateLetterGrade(string score)
        {
            if (int.TryParse(score, out int scoreValue))
            {
                if (scoreValue >= 90) return "A";
                else if (scoreValue >= 80) return "B";
                else if (scoreValue >= 70) return "C";
                else if (scoreValue >= 60) return "D";
                else return "F";
            }
            else
            {
                // Handle the case where score is not a valid integer (e.g., invalid input)
                return "Invalid";
            }
        }
    }

    public class GradeSummary
    {
        public string LetterGrade { get; set; }
        public int TotalCount { get; set; }
    }
}

Here is what is currently showing: enter image description here

I tried changing up the script and am contemplating using Adapters instead of using the method that I am using but I feel like it's an easy fix. The data all comes from CRUD operations on the site and that I added so the data is not static. Exam scores come from another model that has its own CRUD operation (edit.cshtml, edit.cshtml.cs, delete.cshtml...) and this page is used for reporting and charting the exam scores.


Solution

  • Based on the properties in the Letter Grade Summary table, the properties are in Pascal's Case. But in your JavaScript code, it is in camel case. The properties are case-sensitive and thus your graph is not rendered correctly (for example: the labels show undefined).

    Amend the properties in Pascal's Case format.

    const data = {
        labels: gradeData.map(g => g.LetterGrade),
        datasets: [{
            label: 'Grade Distribution',
            data: gradeData.map(g => g.TotalCount),
            backgroundColor: ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'],
            hoverOffset: 4
        }]
    };