1

I am trying to write an app that will allow people to insert data into a form and this will draw a chart via Chart.js I am ok if there are not multiple datasets but when there are I look for a '/' and put values into an array. The problem I am having is I cant seem to get this form data. ie The chart I load initially has the two datasets properly setup. But I have tried using this code:

formData[index].value.forEach(function (value, dataset_index) {
 console.log(formData[index].name);
 chartData.datasets[dataset_index][formData[index].name] = value; // Problem Line

I get error "TypeError: chartData.datasets[dataset_index] is undefined". I have tried multiple permutations on this bracket notation and get similar errors.

// /public/main.js
const chartData = {
 labels: ['M', 'T', 'W', 'T', 'F', 'S', 'S'],
 datasets: [{
 label: 'apples',
 data: [12, 19, 3, 17, 6, 3, 7],
 backgroundColor: "rgba(153,255,51,0.4)"
 }, {
 label: 'oranges',
 data: [2, 29, 5, 5, 2, 3, 10],
 backgroundColor: "rgba(255,153,0,0.4)"
 }]
};
let Options = {
 scales: {
 yAxes: [{
 ticks: {
 beginAtZero: true
 }
 }]
 }
};
const ctx = document.getElementById("myChart").getContext('2d');
let myChart = new Chart(ctx, {
 type: 'bar',
 data: chartData,
 options: Options
});
// Get Chart Info from form
$(document).ready(function() {
 $("#render_btn").on("click", function(e) {
 e.preventDefault();
 // First grab form data off the page
 const formData = $('form').serializeArray();
 // Get Chart Type and Options Seperate from Form Data
 const chartTypeControl = document.getElementById("chart_type");
 const chartType = chartTypeControl.options[chartTypeControl.selectedIndex].value;
 Options = document.getElementById("chart_options").value;
 // Create a data Object for Chart constructor to use
 
 let datasetsItem = {};
 // Convert formData array to chartData object
 formData.forEach(function(value, index) {
 if (formData[index].name == 'labels' || formData[index].name == 'options') {
 chartData[(formData[index].name)] = formData[index].value;
 } else {
 // Check if this form value has multiple datasets(has a '/') and if so
 // split the string into seperate dataset's
 if (formData[index].value.indexOf('/') > -1) {
 // Split the field up into seperate array items
 formData[index].value = splitString(formData[index].value, '/');
 // Now put the array items into their seperate datasets
 formData[index].value.forEach(function (value, dataset_index) {
 datasetsItem[formData[index].name] = value;
 // console.log(datasetsItem[formData[index].name]);
 chartData.datasets[dataset_index] = datasetsItem;
 });
 } else {
 datasetsItem[formData[index].name] = formData[index].value;
 chartData.datasets[0] = datasetsItem;
 }
 }
 });
 // =====================================================================================
 // Now we have to do some converting i.e., chartData.labels must be converted to array 
 // from string etc.. ==================================================================
 chartData.datasets[0].backgroundColor = splitString(chartData.datasets[0].backgroundColor);
 chartData.datasets[0].borderColor = splitString(chartData.datasets[0].borderColor);
 chartData.datasets[0].data = strToNumberArray(chartData.datasets[0].data);
 chartData.labels = splitString(chartData.labels);
 chartData.datasets[0].borderWidth = strToNumber(chartData.datasets[0].borderWidth);
 if (isNaN(chartData.datasets[0].borderWidth)) {
 alert("Attention: Border Width needs to be a number.");
 }
 // Check if successful
 try {
 if (!(chartData.datasets[0].data) || !(chartData.labels)) {
 throw new Error("Input Error. Recheck your form data.");
 }
 myChart.type = chartType;
 myChart.data = chartData;
 myChart.update();
 } catch (error) {
 alert(error);
 }
 // ============================================================= //
 // ============= Function definitions ========================== //
 // ============================================================= //
 function splitString(strToSplit, separator = ",") {
 if (!strToSplit) {
 alert("Error: One of your required fields is empty.");
 return "";
 }
 // Test for a ',' or '/' slash in the string
 const result = /[,\/]/g.test(strToSplit);
 if (!result) {
 // Only one entry in Data form
 return strToSplit;
 }
 // Split a string into an array and trim any whitespace
 let arrayOfStrings = strToSplit.split(separator);
 arrayOfStrings.forEach(function(value, index) {
 arrayOfStrings[index] = value.trim();
 });
 return arrayOfStrings;
 }
 // Function to convert string to an array then convert each element to a number
 function strToNumberArray(str, separator = ',') {
 if (str === undefined) {
 alert('Error: string is empty.');
 return "";
 }
 // Test for a comma in the string
 const result = /,+/.test(str);
 if (!result) {
 alert(`Comma delimiter missing from ${str}`);
 return false;
 }
 let arrayOfNumbers = str.split(separator).map(Number);
 return arrayOfNumbers;
 }
 // Function convert string type to number
 function strToNumber(str) {
 Number(str);
 return str;
 }
 // Functtion remove all whitespace from string
 function removeWhiteSpace(str) {
 str.replace(/\s+/g, '');
 return str;
 }
 // ============================================================== //
 // ================== End Function Definitions ================== //
 // ============================================================== //
 }); // End .on "click"
});
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<canvas id="myChart" width="400" height="400"></canvas>
<h2>Input your values:</h2>
<!-- Chart Input Form -->
<!-- We dont want chart_type as part of form when we use serializeArray
 gather the data and use in chartData object -->
<div class="row">
 <div class="col-md-4 col-md-offset-4">
 <div class="form-group">
 <label for="chart_type">Chart Type</label>
 <input type="text" name="type" class="form-control" id="chart_type" placeholder="Chart Type">
 </div>
 </div>
</div>
<form>
 <div class="row">
 <div class="col-md-4 col-md-offset-4 ">
 <div class="form-group">
 <label for="chart_type">Chart Type</label>
 <select class="form-control input-lg" id="chart_type">
 <option value="bar">Bar Chart</option>
 <option value="pie">Pie Chart</option>
 <option value="line">Line</option>
 <option value="doughnut">Doughnut</option>
 </select>
 </div>
 </div>
 </div>
 <div class="row">
 <div class="col-md-4 col-md-offset-4">
 <div class="form-group">
 <label for="chart_Label">Chart Label</label>
 <input type="text" name="label" class="form-control input-lg" id="chart_Label" placeholder="Chart Label">
 </div>
 </div>
 </div>
 <div class="row">
 <div class="col-md-6 col-md-offset-4">
 <div class="form-group">
 <label for="chart_labels">Chart Labels</label>
 <input type="text" name="labels" class="form-control input-lg" id="chart_labels" placeholder="Apr, May, June Note:Seperated by Commas">
 </div>
 </div>
 </div>
 <div class="row">
 <div class="col-md-6 col-md-offset-4">
 <div class="form-group">
 <label for="chart_data">Chart Data</label>
 <input type="text" name="data" class="form-control input-lg" id="chart_data" placeholder="i.e., 25 44 60 etc">
 </div>
 </div>
 </div>
 <div class="row">
 <div class="col-md-6 col-md-offset-4">
 <div class="form-group">
 <label for="chart_colors">Chart Colors</label>
 <input type="text" name="backgroundColor" class="form-control input-lg" id="chart_colors" placeholder="i.e., red, blue, yellow, purple">
 </div>
 </div>
 </div>
 <!-- ============ More Options Markup ================================== -->
 <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#more_options" aria-expanded="false" aria-controls="collapseExample">
 More Options
 <span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span>
 </button>
 <div class="collapse" id="more_options">
 <div class="well">
 <div class="row">
 <div class="col-md-6 col-md-offset-4">
 <div class="form-group">
 <label for="border_color">Border Color(s)</label>
 <input type="text" name="borderColor" class="form-control input-lg" id="border_color" placeholder="i.e., red,blue,green,orange">
 </div>
 </div>
 <div class="col-md-6 col-md-offset-4">
 <div class="form-group">
 <label for="border_width">Border Width</label>
 <input type="text" name="borderWidth" class="form-control input-lg" id="border_width" placeholder="i.e., 1">
 </div>
 </div>
 </div>
 <!-- Options Markup -->
 <div class="col-md-6 col-md-offset-4">
 <div class="form-group">
 <label for="chart_options">Chart Options</label>
 <input type="text" name="options" class="form-control input-lg" id="chart_options" placeholder="See Chart.js Documentation">
 </div>
 </div>
 </div>
 </div>
</form>
<div class="row">
 <div class="col-md-4 col-md-offset-5">
 <button class="btn btn-primary btn-lg" id="render_btn">
 <span class="glyphicon glyphicon-stats" aria-hidden="true"></span>
 Render Graph
 </button>
 </div>
</div>

Long story short I cant seem to store the apples/oranges or any other values that are in multiple datasets into the chartData object array properly. Any help/insight much appreciated.

Update: Yes I can now see the dataset array in chartData but it is incorrect. See image: Dataset array

It would seem chartData.datasets{0] and 1 are exactly the same?? I updated the snippet. When I console.log datasets in the forEach I get the right values spitting out. Any suggestions ? Thanks.

asked Dec 29, 2017 at 23:31
3
  • I'm getting an error on this line: const chartType = chartTypeControl.options[chartTypeControl.selectedIndex].value;. chartTypeControl is a text input, not a <select>, so it doens't have a selectedIndex`. Commented Dec 29, 2017 at 23:49
  • You have two chartData variables -- a global one at the top, and a local one inside the click handler. Is that intentional? Commented Dec 29, 2017 at 23:59
  • Sorry that was error on my part. See updated snippet. Commented Dec 30, 2017 at 0:53

1 Answer 1

1

From a glance, at your problem line, chartData.datasets[dataset_index] is not defined yet (as the error tells). You need to assign the element at that index. At the point you'll be trying to set the object key via a variable as seen here. So something like this should work:

var data = {};
data[formData[index].name] = value;
chartData.datasets[dataset_index] = data;
answered Dec 29, 2017 at 23:49
Sign up to request clarification or add additional context in comments.

2 Comments

The whole chartData object is initialized at the top of the script. It has an array in chartData.datasets.
Wait but there's another chartData declared inside of the click handler.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.