sslepian
sslepian

Reputation: 1921

directive isolate scope not triggering $watch

I've got a directive that shares data with its controller, which I'm doing with an isolate scope.

However, when I update the data from inside the directive, neither the directive nor the controller sees the data change using $watch.

Here's an example of what I'm trying to do:

var app = angular.module('myApp', []);

app.controller('myAppCtrl', function($scope) {
  $scope.buttonSettings = {
    val: 0
  }

  $scope.$watch('buttonSettings.val', function(val) {
    console.log('watchInController')
    console.log(val)
  });
});

app.directive('buttonTest', function() {
  return {
    restrict: 'E',
    scope: {
      buttonSettings: '='
    },
    template: '<button>{{buttonSettings.val}}</button>',
    link: function(scope) {
      var button = $('button');

      console.log(scope.buttonSettings)

      scope.$watch('buttonSettings.val', function(val) {
        console.log('watchInDirective')
        console.log(val)
      });

      button.bind('mouseover', function() {
        scope.buttonSettings.val++;
        console.log('mouseover')
        console.log(scope.buttonSettings.val)
      })
    }
  }
});

And here's the full example in plunkr: http://plnkr.co/edit/lnztoMhtU03Qzl0BaMpk?p=preview

What am I doing wrong?

Upvotes: 1

Views: 719

Answers (2)

m59
m59

Reputation: 43745

halilb is correct that you should not be using jQuery to select element, but instead use the element that Angular provides to your directive, and that $scope.$apply is needed in order to register events that occur outside of Angular's context. However, you don't at all need to make a new directive to solve your problem here. Just use Angular's ngMouseover directive. You can bind a function to it or just add your logic directly in. Here's a very minimal, simple example.

<div ng-init="foo = 1">Value: {{foo}}</div>
<button ng-mouseover="foo = foo+1">Mouse over this: {{foo}}</button>

Here's how the same code looks with the logic moved to the controller:

<div>Value: {{foo}}</div>
<button ng-mouseover="incrementFoo()">Mouse over this: {{foo}}</button>

controller:

$scope.foo = 1;
$scope.incrementFoo = function() {
  ++$scope.foo;
};

Live demo

Upvotes: 1

halilb
halilb

Reputation: 4115

First of all, you should not use jQuery that way. Instead of selecting button with jQuery, you can just inject it to your directive using element parameter.

Second, you need to use $scope.$apply to trigger watches in your directive and controller.

app.directive('buttonTest', function() {
  return {
    scope: {
      buttonSettings: '='
    },
    link: function(scope, element) {
      element.bind('mouseover', function() {
        scope.$apply(function() {
          scope.buttonSettings.val++;
        });
      })
    }
  }
});

Check out this working plunker.

Upvotes: 1

Related Questions