Search code examples
javascriptangularjsd3.jsnvd3.jsangular-nvd3

nvd3.js - display specific color for each bar in multibar vertical chart depending on range of value


As the topic says, I simply want to customize the color of each bar based on the value of the y axis.

The range of value-colors are:

  • 0-50: green
  • 50-75: yellow
  • 75-100: red

I tried to search this topic on Google a lot but could not find any results.

This stackoverflow question was the closest to my answer however it's not for nvd3. If in case it is, please do tell me which location I am supposed to add the functions.

My codes are as follow:-

HTML:-

<nvd3 options="options" data="data2"></nvd3>

JS:-

/* Chart options */
$scope.options = { /* JSON data */

          chart: {
              type: 'multiBarChart',
              height: 250,
              showControls: false,
              margin : {
                  top: 20,
                  right: 20,
                  bottom: 45,
                  left: 45
              },
              clipEdge: true,
              duration: 500,
              stacked: true,
              xAxis: {
                  axisLabel: 'Time (ms)',
                  showMaxMin: false,
                  tickFormat: function(d){
                      return d3.format(',f')(d);
                  }
              },
              yAxis: {
                  axisLabel: 'Y Axis',
                  axisLabelDistance: -20,
                  tickFormat: function(d){
                      return d3.format(',.1f')(d);
                  }
              }
          },

// title options
title: {
    enable: true,
    text: 'Title for Line Chart'
},

// subtitle options
subtitle: {
    enable: true,
    text: 'Subtitle for simple line chart. Lorem ipsum dolor sit amet...',
    css: {
        'text-align': 'center',
        'margin': '10px 13px 0px 7px'
    }
},

// caption options
caption: {
    enable: true,
    html: 'Figure 1. Lorem ipsum dolor sit amet...',
    css: {
        'text-align': 'justify',
        'margin': '10px 13px 0px 7px'
    }
   }
 };

 /* Chart data */
 $scope.data2 = [{"key":"Thermal","values":[{"x":0,"y":44},{"x":1,"y":24},{"x":2,"y":66},{"x":3,"y":10},{"x":4,"y":33}]}];

Solution

  • After finding code in this answer and the code in responses from this github issue, it was found that d3.selectAll() can be utilized in the callback option for the chart.

    $scope.options = {
        chart: {
            type: 'multiBarChart',
            //...,
            callback: function(){
                d3.selectAll('rect.nv-bar')
                .style('fill', function(d, i) {
                      if (d.y > 75) {
                        return 'red';
                      }
                      if (d.y > 50) {
                        return 'yellow';
                      }
                      return 'green';
                    });
                }
            }
        }
    }
    

    See a demonstration of the changes applied by running the code in the snippet below.

    var app = angular.module('plunker', ['nvd3']);
    app.controller('MainCtrl', function($scope) {
      $scope.options = { /* JSON data */
        chart: {
          type: 'multiBarChart',
          height: 250,
          showControls: false,
          margin: {
            top: 20,
            right: 20,
            bottom: 45,
            left: 45
          },
          clipEdge: true,
          duration: 500,
          stacked: true,
          xAxis: {
            axisLabel: 'Time (ms)',
            showMaxMin: false,
            tickFormat: function(d) {
              return d3.format(',f')(d);
            }
          },
          yAxis: {
            axisLabel: 'Y Axis',
            axisLabelDistance: -20,
            tickFormat: function(d) {
              return d3.format(',.1f')(d);
            }
          },
          callback: function() {
            //console.log('in callback');
            d3.selectAll('rect.nv-bar')
              .style('fill', function(data, index) {
                  //console.log('data.y: ',data.y);
                if (data.y > 75) {
                  return 'red';
                }
                if (data.y > 50) {
                  return 'yellow';
                }
                return 'green';
              });
          }
        },
    
        // title options
        title: {
          enable: true,
          text: 'Title for Line Chart'
        },
    
        // subtitle options
        subtitle: {
          enable: true,
          text: 'Subtitle for simple line chart. Lorem ipsum dolor sit amet...',
          css: {
            'text-align': 'center',
            'margin': '10px 13px 0px 7px'
          }
        },
    
        // caption options
        caption: {
          enable: true,
          html: 'Figure 1. Lorem ipsum dolor sit amet...',
          css: {
            'text-align': 'justify',
            'margin': '10px 13px 0px 7px'
          }
        }
      };
    
      /* Chart data */
      $scope.data2 = [{
        "key": "Thermal",
        "values": [{
          "x": 0,
          "y": 44
        }, {
          "x": 1,
          "y": 24
        }, {
          "x": 2,
          "y": 66
        }, {
          "x": 3,
          "y": 10
        }, {
          "x": 4,
          "y": 33
        }]
      }];
    });
    <link rel="stylesheet" href="style.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.min.css" />
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-nvd3/1.0.5/angular-nvd3.min.js"></script>
    <div ng-app="plunker" ng-controller="MainCtrl">
      <nvd3 options="options" data="data2"></nvd3>
    </div>