5

Just starting out in AngularJS and trying to figure out the best practice for listening to events when a variable within a controller changes. The only way I have gotten it to work is with an emit, as follows.

For example:

var app = angular.module("sampleApp", [])

app.controller("AppCtrl", function($scope){
  $scope.elements = [
    {
        name: "test"
    }
  ]

  $scope.addElement = function() {
      $scope.elements.push({
          name: "test" + $scope.elements.length
      })
      $scope.$emit('elementsChanged', $scope.elements);
  }
})

app.directive('render', function() {
  var renderFunc = function() {
      console.log("model updated");
  }
  return {
      restrict: 'E',
      link: function(scope, element, attrs, ngModel) {
         scope.$on('elementsChanged', function(event, args) {
              renderFunc();
         })
      }
  }
})

This seems a bit wonky, and I feel like I'm working against the point of angular. I've tried to have a $watch on a model, but that doesn't seem to be working. Any help on this would be very appreciated, thanks!

dardo
  • 4,870
  • 4
  • 35
  • 52
  • Is your goal to inform other controllers that the variable has changed? or? – KayakDave Oct 22 '13 at 21:31
  • Just directives at this point, just noticed my title had a weird typo, haha. – dardo Oct 22 '13 at 21:31
  • Check this out and see if it's what you're looking for: http://stackoverflow.com/questions/17138868/how-to-trigger-a-directive-when-updating-a-model-in-angularjs – KayakDave Oct 22 '13 at 21:35
  • @KayakDave similar, Adam actually summed it up perfectly for me, thanks though! – dardo Oct 22 '13 at 21:48

1 Answers1

3

I'm going to assume you're using unstable Angular, because $watchCollection is only in the unstable branch.

$watchCollection(obj, listener)

Shallow watches the properties of an object and fires whenever any of the properties change (for arrays, this implies watching the array items; for object maps, this implies watching the properties). If a change is detected, the listener callback is fired.

The 'Angular' way of doing this would be to watch an attribute in your directive.

<render collection='elements'></render>

Your directive

app.directive('render', function() {
  var renderFunc = function() {
      console.log("model updated");
  }
  return {
      restrict: 'E',
      link: function(scope, element, attrs) {
         scope.$watchCollection(attrs.collection, function(val) {
           renderFunc();
         });
      }
  }
})

If you're doing this on stable angular, you can pass true as the last argument to scope.$watch, which will watch for equality rather than reference.

$watch(watchExpression, listener, objectEquality)

objectEquality (optional) boolean Compare object for equality rather than for reference.

What's happening here is the collection attribute on the DOM element specifies which property on our scope we should watch. The $watchCollection function callback will be executed anytime that value changes on the scope, so we can safely fire the renderFunc().

Events in Angular should really not be used that often. You were right in thinking there was a better way. Hopefully this helps.

Adam
  • 1,143
  • 7
  • 7
  • This is exactly what I was looking for! I knew events felt really wonky. I actually am not using the unstable release, so ended up just passing true as the final argument, and it worked like a charm. Thanks a bunch! – dardo Oct 22 '13 at 21:46