0

I am using angular-nvd3 in a project. It exposes an element directive that looks like so:

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

This renders a chart according to the options and data values. It has two way data binding for the options and data attributes. When you change the data or options, the chart updates.

I have been trying to wrap this directive in my own directive, to expose less functionality with a different interface (following along https://www.airpair.com/angularjs/posts/component-based-angularjs-directives)

ngModule.directive('myDirective', [function() {
  template: '<div nvd3 options="ctrl.options" data="ctrl.data"></div>',
  scope: {},
  bindToController: {
    w: '=',
    h: '='
  },
  controllerAs: 'ctrl',
  controller: ['$scope', function($scope) {
    var ctrl = this;
    $scope.$watch(function() { return ctrl.w; }, render );
    $scope.$watch(function() { return ctrl.h; }, render );

    function render() {
      ctrl.data = ctrl.data || {}       # omitted object definition
      ctrl.options = ctrl.options || {} # omitted object definition
      ctrl.options.chart.width = ctrl.w;
      ctrl.options.chart.height = ctrl.h;
    }

  }]
}]);

This works fine for the initial render. However, when testing, I cannot seem to get the chart to update when I modify the w or h parameters.

describe('my-directive', function() {

  var scope, element, render;

  function getSvgAttr(el, attr) {
    return el.find('svg')[0].getAttribute(attr);
  }

  beforeEach(inject(function($rootScope, $compile) {
    scope = $rootScope.$new();
    var compileFn = $compile(
      '<div my-directive w="w" h="h">'
    );
    render = function() {
      element = compileFn(scope);
       $rootScope.$digest();
    }
  }));

  it('should resize', function() {
    scope.w = 400;
    scope.h = 200;
    render();

    var ctrl = element.isolateScope().ctrl();

    expect(ctrl.w).toBe(400);
    expect(ctrl.h).toBe(200);

    expect(getSvgAttr(element, 'width')).toBe('400');
    expect(getSvgAttr(element, 'height')).toBe('200');

    scope.w = 200;
    scope.h = 400;
    scope.$digest();

    expect(ctrl.w).toBe(200);
    expect(ctrl.h).toBe(400);

    expect(ctrl.w).toBe(200);
    expect(ctrl.h).toBe(400);

    expect(getSvgAttr(element, 'width')).toBe('200');
    expect(getSvgAttr(element, 'height')).toBe('400');

  });

});

It is the last two assertions that fail. Playing around with angular-nvd3, modifying options and data modify the graph.

However, when I add a wrapper directive the scope appears to change but the graph does not.

Any help would be greatly appreciated! I was under the impression that with bindToController I wouldn't need any of those $scope.$watch lines either.

dacox
  • 502
  • 1
  • 6
  • 14

1 Answers1

0

It appears that in your controller above, you repeat the following line of code:

$scope.$watch(function() { return ctrl.w; }, render );

I would assume you meant to alter the second iteration to the following:

$scope.$watch(function() { return ctrl.h; }, render );

As a result, the program is tripping in double-watching the 'w' parameter and is not watching for changes to the 'h' parameter.

This may only be part of the problem, however.

Hope it helps,

CSS
  • 412
  • 5
  • 17
  • hmm, yes I'll fix that. I believe that was a typo in transcription - both watches are set in my code. – dacox Jul 30 '15 at 19:02