Search code examples
javascriptjquerymorris.js

Setting variable data to morris.bar


I have to draw a bar chart with Morris, but data is dynamic in such a way that I don't know how many labels I am going to draw, etc...

Normally you build such a chart with:

Morris.Bar({
  element: 'bar-example',
  data: [
    { y: '2006', a: 100, b: 90 },
    { y: '2007', a: 75,  b: 65 },
    { y: '2008', a: 50,  b: 40 },
    { y: '2009', a: 75,  b: 65 },
    { y: '2010', a: 50,  b: 40 },
    { y: '2011', a: 75,  b: 65 },
    { y: '2012', a: 100, b: 90 }
  ],
  xkey: 'y',
  ykeys: ['a', 'b'],
  labels: ['Series A', 'Series B']
});
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
<div id="bar-example" style="height: 250px;"></div>

But I don't know if I am having a and b, or a, b and c, or a, b, c and d or even more. How do I achieve this?

The data I am passing is a class that contains a label and a list of values, so I can draw multiple bars per label. The problem here is that I don't know how many bars I am drawing as the data is generated at runtime.

Edit:

I pass the data from the controller like this:

[{"label":"Cats","values":[57,92,94]},{"label":"Dogs","values":[85,78,53]},{"label":"Birds","values":[81,57,70]}]

So far I managed to draw the labels if I send them as an array of Strings, but if I send an array of 3 elements, it shows the first elements as they are but then adds three more "Undefined" labels.

With that data structure I get to see "Cats", "Dogs" and "Birds" on the x axis if I set:

xkey: 'label',

Solution

  • Looking at my graph I don't think I quite captured what you are trying to display but hopefully it will get the wheels spinning. You can fetch the data via ajax and then create the chart on the fly by calling the function defined below as createChart(). I've made a dummy self-executing function that passes the data in you example:

    // dummy function to call createChart()
    (function() {
      let data = `[
      { "label": "Cats", "values": [57, 92, 94] },
      { "label": "Dogs", "values": [85, 78, 53] },
      { "label": "Birds", "values": [81, 57, 70] }]`;
      createChart(data);
    })();
    
    function createChart(data) {
      data = JSON.parse(data);
      let labels = data.map(obj => obj.label);
    
      // Generte n keys
      let ykeys = data[0].values.map((obj, i) => 'val' + i);
      let values = data.map(function(obj) {
        let map = {
          label: obj.label
        }
        obj.values.forEach(function(obj, i) {
          map[ykeys[i]] = obj;
        });
        return map;
      });
    
      Morris.Bar({
        element: 'bar-example',
        data: values,
        xkey: 'label',
        ykeys: ykeys,
        labels: labels
      });
    }
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
    <div id="bar-example" style="height: 250px;"></div>