I've created a live poll using Chart.js and want the user to be able to select their option by selecting a checkbox to the left of the y-axis label. I'm having trouble figuring out how to do this the right way with Chart.js
My first attempt was to place the input box outside of the canvas but it's highly unreliable with dynamic poll options as they grow. It's pretty bad because the height of the bar chart will vary depending on how many options there are so the input boxes become a mess as the options displayed are dynamic.
Here is an image of what I'm trying to ultimately achieve on the front-end. If the user selects the box then the poll grows to reflect the choice the user has selected.
Posted below is my code of where I'm currently stuck. I've been stuck trying to figure this out for quite some time now. Any help would be greatly appreciated!
import { Component, OnInit } from '@angular/core';
// import { Chart } from 'chart.js';
import * as Ably from 'ably';
import * as Chart from 'chart.js';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable, Subject } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { FirebaseService } from '../services/firebase.service';
@Component({
selector: 'app-vote-chart',
templateUrl: './vote-chart.component.html',
styleUrls: ['./vote-chart.component.scss']
})
export class VoteChartComponent implements OnInit {
// Attributes
ably: any
receiveChanel: any
chart: any
users: any
polls:any = [];
poll:any;
votes:any = [];
labels:any = [];
poll_type:string = "";
constructor(db: AngularFirestore, private firebaseService: FirebaseService) {}
ngOnInit() {
this.firebaseService.getPoll("key").subscribe(res => {
const poll_data:any = res.payload.data();
this.poll = {
id: res.payload.id,
helper_text: poll_data.helper_text,
poll_type: poll_data.poll_type,
scoring_type: poll_data.scoring_type,
user: poll_data.user.id,
choices: []
};
this.poll_type = this.poll.poll_type == 2 ? "Pick One" : "Pick Two";
this.firebaseService.getChoices('key').subscribe(res => {
res.forEach((choice) => {
const choice_data:any = choice.payload.doc.data()
this.poll.choices.push({
id: choice.payload.doc.id,
text: choice_data.text,
votes: choice_data.votes
});
this.votes.push(choice_data.votes);
this.labels.push(choice_data.text);
});
console.log("Poll updated!", this.poll);
});
});
this.ably = new Ably.Realtime("key")
// Attach to channel
this.receiveChanel = this.ably.channels.get('vote-channel12')
// Ably subscription
this.receiveChanel.subscribe("update", (message: any) => {
var canvas = <HTMLCanvasElement> document.getElementById("chartjs-2")
var ctx = canvas.getContext("2d");
this.chart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: this.labels,
datasets: [{
label: this.poll_type,
data: this.votes,
fill: false,
backgroundColor: [
"rgba(255, 99, 132, 0.2)",
"rgba(255, 159, 64, 0.2)",
"rgba(255, 205, 86, 0.2)",
"rgba(75, 192, 192, 0.2)",
"rgba(54, 162, 235, 0.2)",
"rgba(153, 102, 255, 0.2)",
"rgba(201, 203, 207, 0.2)"
],
borderColor: [
"rgb(255, 99, 132)",
"rgb(255, 159, 64)",
"rgb(255, 205, 86)",
"rgb(75, 192, 192)",
"rgb(54, 162, 235)",
"rgb(153, 102, 255)",
"rgb(201, 203, 207)"
],
borderWidth: 1
}]
},
options: {
events: ["touchend", "click", "mouseout"],
onClick: function(e) {
console.log("clicked!", e);
},
tooltips: {
enabled: true
},
title: {
display: true,
text: this.poll_type,
fontSize: 14,
fontColor: '#666'
},
legend: {
display: false
},
maintainAspectRatio: true,
responsive: true,
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
precision: 0
}
}]
}
}
})
console.log("Poll", this.poll);
});
}
}
Template code, inline styled to make it easier to review
<div id="chart_and_labels_container" style="min-width:620px !important;">
<div style="float:left; line-height: 175px; display: inline-grid; padding-top: 55px; margin: 0;">
<input type="checkbox" (click)="vote(1)" style="width:57px; height: 30px;"/>
<input type="checkbox" (click)="vote(-1)" style="width:57px; margin-top: 10px; height: 30px;"/>
<input type="checkbox" (click)="vote(-1)" style="width:57px; margin-top: 10px; height: 30px;"/>
</div>
<div id="canvasDiv" style="width: 425px; float:left;">
<canvas id="chartjs-2" ></canvas>
</div>
</div>
** UPDATE **
If it's not possible to get the input boxes in the canvas can someone offer some advice on the best way to make sure the input boxes line up reliably with the dynamic choices? I.e the text size for the labels is different for 2 options vs when you have 5 options. How do I line up the input boxes outside of the canvas in an intelligent and reliable way?