richardnixonthethird
richardnixonthethird

Reputation: 345

Automatically Resizing Angular-nvd3 chart inside of Angular Material Grid

I am trying to get a responsive nvd3 chart inside of an angular material grid. It seems to be working ok after I resize the window but does not go to the correct size at the initial load.

My approach was to wrap the nvd3 directive inside of a 'resize' directive. Then I could watch the parent element and update the chart's height and width on window resize. The problem I am running into is on load the parent div height and width properties seem to be 0. I tried using $timeout to apply the scope but this did not seem to work either.

angular.module('MyApp', ['ngMaterial', 'ngMessages', 'nvd3'])
  .controller('AppCtrl', function($scope) {
    $scope.options = {
      chart: {
        type: 'discreteBarChart',
        height: 450,
        margin: {
          top: 20,
          right: 20,
          bottom: 50,
          left: 55
        },
        x: function(d) {
          return d.label;
        },
        y: function(d) {
          return d.value + (1e-10);
        },
        showValues: true,
        valueFormat: function(d) {
          return d3.format(',.4f')(d);
        },
        duration: 500,
        xAxis: {
          axisLabel: 'X Axis'
        },
        yAxis: {
          axisLabel: 'Y Axis',
          axisLabelDistance: -10
        }
      }
    };

    $scope.data = [{
      key: "Cumulative Return",
      values: [{
        "label": "A",
        "value": -29.765957771107
      }, {
        "label": "B",
        "value": 0
      }, {
        "label": "C",
        "value": 32.807804682612
      }, {
        "label": "D",
        "value": 196.45946739256
      }, {
        "label": "E",
        "value": 0.19434030906893
      }, {
        "label": "F",
        "value": -98.079782601442
      }, {
        "label": "G",
        "value": -13.925743130903
      }, {
        "label": "H",
        "value": -5.1387322875705
      }]
    }]
  }).directive('resize', function($window, $timeout) {
    var linker = function(scope, element, attrs) {
      var w = angular.element($window);
      
      w.bind('resize', function() {
        scope.$apply();
      })
      
      $timeout(function(){
        updateHeight();
        updateWidth();
        scope.$apply();
      })

      scope.$watch(function() {
        return element.parent()[0].clientHeight;

      }, updateHeight);

      scope.$watch(function() {
        return element.parent()[0].clientWidth;

      }, updateWidth);

      function updateHeight() {
        scope.options.chart.height = element.parent()[0].clientHeight;
      }

      function updateWidth() {
        scope.options.chart.width = element.parent()[0].clientWidth;
      }
    };

    return {
      template: '<div style="height100%;width:100%;"><nvd3 options="options" data="data"></nvd3></div>',
      restrict: "E",
      link: linker,
      scope: {
        data: '=',
        options: '='
      }
    };
  });
.gridListdemoBasicUsage md-grid-list {
  margin: 8px; }

.gridListdemoBasicUsage .gray {
  background: #f5f5f5; }

.gridListdemoBasicUsage .green {
  background: #b9f6ca; }

.gridListdemoBasicUsage .yellow {
  background: #ffff8d; }

.gridListdemoBasicUsage .blue {
  background: #84ffff; }

.gridListdemoBasicUsage .purple {
  background: #b388ff; }

.gridListdemoBasicUsage .red {
  background: #ff8a80; }

.gridListdemoBasicUsage md-grid-tile {
  transition: all 400ms ease-out 50ms; }
<div ng-controller="AppCtrl as appCtrl" ng-cloak="" class="gridListdemoBasicUsage" ng-app="MyApp">
  <md-grid-list md-cols-xs="1" md-cols-sm="2" md-cols-md="4" md-cols-gt-md="6" md-row-height-gt-md="1:1" md-row-height="2:2" md-gutter="12px" md-gutter-gt-sm="8px">
    <md-grid-tile class="gray" md-rowspan="3" md-colspan="2" md-colspan-sm="1">
      <resize options="options" data="data"></resize>
      <md-grid-tile-footer>
        <h3>#1: (3r x 2c)</h3>
      </md-grid-tile-footer>
    </md-grid-tile>

    <md-grid-tile class="green">
      <md-grid-tile-footer>
        <h3>#2: (1r x 1c)</h3>
      </md-grid-tile-footer>
    </md-grid-tile>

    <md-grid-tile class="yellow">
      <md-grid-tile-footer>
        <h3>#3: (1r x 1c)</h3>
      </md-grid-tile-footer>
    </md-grid-tile>

    <md-grid-tile class="blue" md-rowspan="2">
      <md-grid-tile-footer>
        <h3>#4: (2r x 1c)</h3>
      </md-grid-tile-footer>
    </md-grid-tile>

    <md-grid-tile class="red" md-rowspan="2" md-colspan="2" md-colspan-sm="1">
      <md-grid-tile-footer>
        <h3>#5: (2r x 2c)</h3>
      </md-grid-tile-footer>
    </md-grid-tile>

    <md-grid-tile class="green" md-rowspan="2">
      <md-grid-tile-footer>
        <h3>#6: (2r x 1c)</h3>
      </md-grid-tile-footer>
    </md-grid-tile>

  </md-grid-list>
</div>

please check out this codepen.

Thanks in advance

Update: I found a solution for my app using Angular nvd3s api.updateWithOptions(options). Still not working in the codepen. Could be a version difference in the code pen will attach final solution shortly.

Upvotes: 0

Views: 1217

Answers (1)

tim
tim

Reputation: 378

I had the same issue with angular-nvd3 and angular-material. I can not explain why this happen and it is already reported as bug since June 2016 (see https://github.com/krispo/angular-nvd3/issues/454).

However, I was able to fix it with a simple

angular.element(document).ready(function () {})

block around $scope.data.

This example works for me:

Controller

angular.module('app')

.controller('pieChartCtrl', function($scope){

    $scope.options = {
        chart: {
            type: 'pieChart',
            height: 500,
            x: function(d){return d.key;},
            y: function(d){return d.y;},
            showLabels: true,
            duration: 500,
            labelThreshold: 0.01,
            labelSunbeamLayout: true,
            legend: {
                margin: {
                    top: 5,
                    right: 35,
                    bottom: 5,
                    left: 0
                }
            }
        }
    };

    angular.element(document).ready(function () {
        $scope.data = [
            {
                key: "One",
                y: 5
            },
            {
                key: "Two",
                y: 2
            },
            {
                key: "Three",
                y: 9
            },
            {
                key: "Four",
                y: 7
            },
            {
                key: "Five",
                y: 4
            },
            {
                key: "Six",
                y: 3
            },
            {
                key: "Seven",
                y: .5
            }
           ];
    });    
})

View

  <div ng-controller="pieChartCtrl">
      <nvd3 options="options" data="data"></nvd3>
   </div>

Upvotes: 1

Related Questions