2

In my attempts at having angularjs and d3js working together smoothly, I run in trouble when I try to watch an attribute value right within a directive. Let me illustrate with those two simple directives:

.directive('attr1', function($compile) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var attr3val = 0;
      d3.select(element[0])
        .attr('attr2', true)
        .attr('attr3', attr3val)
        .on('click', function() {
          d3.select(element[0])
            .attr('attr3', function() {
              attr3val++;
              console.log("new attr3 value is", attr3val);
              return attr3val;
            });
        });

      element.removeAttr("attr1");
      $compile(element)(scope);
    }
  };
})

.directive('attr2', function() {
  return {
    restrict: 'A',
    scope: {
      attr3: "=attr3"
    },
    link: function(scope, element, attrs) {
      scope.$watch('attr3', function() {
        console.log('attr3 modification trigger');
      });
    }
  };
});

They are shown in action here - console logs have to be activated to understand clearly what happens.

Following this thread, I use $compile so that the attr2 directive is effectively bound after the DOM is modified by d3 calls. I then would like to watch the changes of another attribute (attr3 here). Using a private scope in attr2, I thought this to be possible, but I must be doing something wrong, as clicks in the blue square do indeed update the attribute value, but without the watch being triggered.

Maybe DOM attributes are not "watchable"? Are the only viable solutions those prescribed here?

Thanks by advance for your keen help!

EDIT

The solution using $broadcast shown hereunder is working strictly speaking, but is ineffective if the attribute is common to many elements, and I want to watch one specifically. I'm thus going for a $watch on a function evaluation that uses jQuery, as shown here.

Community
  • 1
  • 1
Pierrick Bruneau
  • 147
  • 1
  • 10
  • when you change scope values outside of angular you need to use `$apply()` – charlietfl Jul 04 '14 at 11:59
  • There is no need for `$apply()` here, as the scope is not involved. attr3val is maintained in the scope of the link function (in the JS sense). And as the plunk update shows, its value is properly incremented. The idea was to bind properties in a private scope to DOM attributes, not to values of an external scope (I don't know if this is clear enough ^^) – Pierrick Bruneau Jul 04 '14 at 12:25

2 Answers2

1

One way to solve this problem is to create custom scope events via $broadcast() within your directives and then attach a listener to the receiving directive.

PLUNKER

e.g.

.directive('attr1', function($compile) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var attr3val = 0;
      d3.select(element[0])
        .attr('attr2', true)
        .attr('attr3', attr3val++)
        .on('click', function() {
          d3.select(element[0])
            .attr('attr3', attr3val++);
            scope.$broadcast('attr3-change', attr3val);
        });

      element.removeAttr("attr1");
      $compile(element)(scope);

      scope.$broadcast('attr3-change', attr3val);
    }
  };
})

.directive('attr2', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {

      scope.$on('attr3-change', function(event, args) {
        console.log(args);
      })

    }
  };
});
ryeballar
  • 29,658
  • 10
  • 65
  • 74
  • I updated mine accordingly - I actually perform my tests with ng-app correctly set, but I must have forgotten to save, my bad. In other words, the problem persists: when clicking, nothing happens. – Pierrick Bruneau Jul 04 '14 at 12:09
  • I have changed my answer accordingly as well – ryeballar Jul 04 '14 at 12:46
  • Your solution does the job quite elegantly indeed! I'm waiting a bit to see if someone comes up with a pure `$watch()` solution, but I guess binding scope variables to DOM attributes is not possible somehow. – Pierrick Bruneau Jul 06 '14 at 16:01
1

If you want to $watch an attribute, you can use $observe:

link: function(scope, element, attrs) {
  attrs.$observe('attr3', function(newValue) {
    console.log('attr3 is ', newValue);
  });
}
Michael Kang
  • 52,003
  • 16
  • 103
  • 135
  • Thanks for the suggestion - but as [this plunk fork](http://plnkr.co/edit/2YUjsusMICbIpTANE6MU?p=preview), this is not effective... – Pierrick Bruneau Jul 04 '14 at 12:22