Building a Dynamic test performance Graph in Angular with Chart.js
Chandan Kumar
Full-Stack Developer | Passionate about innovative web solutions! Angular Developer | MEAN Stack Developer
In this blog post, we'll walk through the process of building a dynamic performance graph for an aptitude test application using Angular and Chart.js. This example will demonstrate how to visualize test results stored in the browser's local storage and present them in a bar chart along with a data table. We’ll cover the following steps:
1. Setting up the Angular Component
First, let's create an Angular component named AptitudeTestGraphComponent. This component will be responsible for displaying the performance graph and the corresponding data table.
import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
import { Chart } from 'chart.js/auto';
import { ChartOptions, ChartConfiguration } from 'chart.js/auto';
@Component({
selector: 'app-aptitude-test-graph',
templateUrl: './aptitude-test-graph.component.html',
styleUrls: ['./aptitude-test-graph.component.css']
})
export class AptitudeTestGraphComponent implements AfterViewInit {
@ViewChild('barChart') barChart!: ElementRef;
userAnswers: any[] = [];
correctAnswers: any[] = [];
performanceLevel!: string;
attemptedQuestions: any[] = [];
testName: any;
currentQuestionIndex: number = 0;
showSubmitButton: boolean = false;
constructor() { }
ngOnInit(): void {
this.getStoredResults();
}
ngAfterViewInit(): void {
this.showCharts();
}
2. Fetching Stored data
The getStoredResults method retrieves the quiz results from localStorage and parses them into a usable format.
getStoredResults() {
const resultsString = localStorage.getItem('quizResults');
if (resultsString) {
const results = JSON.parse(resultsString);
this.attemptedQuestions = results.attemptedQuestions;
this.userAnswers = results.userAnswers;
this.performanceLevel = results.performanceLevel;
this.correctAnswers = results.correctAnswers;
console.log('Stored Quiz Results:', results);
} else {
console.log('No quiz results found in localStorage.');
}
}
3. Processing Data for Visualization
The getTopicData method processes the retrieved data to calculate the correct, incorrect, not attempted counts, and a formula value for each topic.
领英推荐
getTopicData(): { topicName: string, correctCount: number, incorrectCount: number, notAttemptedCount: number, totalCount: number, formulaValue: number, notPerformed: boolean }[] {
const topicCounts: { [key: string]: { correctCount: number, incorrectCount: number, notAttemptedCount: number, totalCount: number, formulaValue: number, notPerformed: boolean } } = {};
for (const q of this.attemptedQuestions) {
const topicName = q.topicName;
if (topicName) {
if (!topicCounts[topicName]) {
topicCounts[topicName] = { correctCount: 0, incorrectCount: 0, notAttemptedCount: 0, totalCount: 0, formulaValue: 0, notPerformed: false };
}
if (q.userAnswer.answer === true) {
topicCounts[topicName].correctCount++;
} else if (q.userAnswer.answer === false) {
topicCounts[topicName].incorrectCount++;
} else {
topicCounts[topicName].notAttemptedCount++;
}
topicCounts[topicName].totalCount++;
topicCounts[topicName].formulaValue = topicCounts[topicName].correctCount / topicCounts[topicName].totalCount * 10;
topicCounts[topicName].notPerformed = topicCounts[topicName].correctCount === 0 && topicCounts[topicName].totalCount !== 0;
}
}
return Object.entries(topicCounts).map(([topicName, { correctCount, incorrectCount, notAttemptedCount, totalCount, formulaValue, notPerformed }]) => ({ topicName, correctCount, incorrectCount, notAttemptedCount, totalCount, formulaValue, notPerformed }));
}
4. Creating the Chart
The showTopicChart method takes the processed data and renders a bar chart using Chart.js. It also appends a data table below the chart for a detailed view.
showCharts() {
if (!this.attemptedQuestions.length) return;
const topicData = this.getTopicData();
console.log('Topic Data:', topicData);
this.showTopicChart(topicData);
}
showTopicChart(topicData: { topicName: string, correctCount: number, incorrectCount: number, notAttemptedCount: number, totalCount: number, formulaValue: number, notPerformed: boolean }[]) {
const topicChartCtx = this.barChart.nativeElement.getContext('2d');
const labels = topicData.map(entry => entry.topicName );
const formulaValues = topicData.map(entry => entry.formulaValue);
const notPerformedData = topicData.map(entry => entry.notPerformed ? 0.1 : 0);
const chartData = {
labels,
datasets: [
{
label: 'Topic Score',
data: formulaValues,
backgroundColor: '#007bff'
},
{
label: 'Topic Not Performed',
data: notPerformedData,
backgroundColor: '#C40013'
}
]
};
const chartOptions: ChartOptions<'bar'> = {
responsive: false,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
},
plugins: {
legend: {
display: true,
position: 'bottom'
}
}
};
const chartConfig: ChartConfiguration<'bar', number[], string> = {
type: 'bar',
data: chartData,
options: chartOptions
};
const chart = new Chart(topicChartCtx, chartConfig);
5. Generating the Data Table
Below the chart, we create a data table to provide a detailed view of the test results.
// Create the data table
const tableContainer = document.createElement('div');
tableContainer.className = 'table-container';
const table = document.createElement('table');
table.className = 'table table-striped table-bordered';
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
['Topic', 'Correct', 'Incorrect', 'Not Attempted', 'Total', 'Formula Value'].forEach(header => {
const th = document.createElement('th');
th.textContent = header;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
const tbody = document.createElement('tbody');
topicData.forEach(entry => {
const row = document.createElement('tr');
const topicCell = document.createElement('td');
topicCell.textContent = entry.topicName;
row.appendChild(topicCell);
const correctCell = document.createElement('td');
correctCell.textContent = entry.correctCount.toString();
row.appendChild(correctCell);
const incorrectCell = document.createElement('td');
incorrectCell.textContent = entry.incorrectCount.toString();
row.appendChild(incorrectCell);
const notAttemptedCell = document.createElement('td');
notAttemptedCell.textContent = entry.notAttemptedCount.toString();
row.appendChild(notAttemptedCell);
const totalCell = document.createElement('td');
totalCell.textContent = entry.totalCount.toString();
row.appendChild(totalCell);
const formulaValueCell = document.createElement('td');
formulaValueCell.textContent = entry.formulaValue.toFixed(2);
row.appendChild(formulaValueCell);
tbody.appendChild(row);
});
table.appendChild(tbody);
tableContainer.appendChild(table);
const chartContainer = this.barChart.nativeElement.parentElement;
chartContainer.appendChild(tableContainer);
}
6. HTML
<div class="chart-container">
<div class="container">
<div class="row">
<div class="col-md-12">
<canvas #barChart></canvas>
</div>
</div>
</div>
</div>
Conclusion
In this post, we demonstrated how to create a dynamic aptitude test performance graph in Angular using Chart.js. By fetching data from localStorage, processing it for visualization, and rendering both a bar chart and a detailed data table, we can provide users with clear insights into their test performance. This approach can be extended to other types of data visualizations and applications, offering a robust solution for presenting analytical results in a user-friendly manner.