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.

Alan
  • 1,067
  • 1
  • 23
  • 37
  • I'm getting an error on this line: `const chartType = chartTypeControl.options[chartTypeControl.selectedIndex].value;`. `chartTypeControl is a text input, not a ` – Barmar Dec 29 '17 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? – Barmar Dec 29 '17 at 23:59
  • Sorry that was error on my part. See updated snippet. – Alan Dec 30 '17 at 00:53

1 Answers1

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;
csp713
  • 412
  • 3
  • 6
  • The whole `chartData` object is initialized at the top of the script. It has an array in `chartData.datasets`. – Barmar Dec 29 '17 at 23:51
  • Wait but there's another `chartData` declared inside of the click handler. – csp713 Dec 29 '17 at 23:55