John Abraham
John Abraham

Reputation: 18801

Why am I unable to watch a service's property from controller?

LIVE CODE: Here

I have injected a service called FileTraversal added to a module divkick.services. I Also have a module divkick.container with a controller named ContainerCtrl.

I've added a watcher function in ContainerCtrl and was hopeful it could watch FileTraversal.stuff array.

Question: I feel this is dumb syntax problem, How do I watch FileTraversal.stuff from ContrainerCtrl

Main App:

(function () {
  "use strict";
  angular.module('divkick', [
    'divkick.services',
    'divkick.components',
    'divkick.container'
  ]);
})();

Controller:

(function () {
  angular
      .module('divkick.container', [])
      .controller('ContainerCtrl', ContainerCtrl)

  ContainerCtrl.$inject = ['FileTraversal', '$scope', '$timeout'];
  /* @ngInject */
  function ContainerCtrl(FileTraversal, $scope, $timeout) {
    /* jshint validthis: true */
    var main = this;

    activate();

    ////////////////

    function activate() {
      $scope.$watch(
          // This function returns the value being watched. It is called for each turn of the $digest loop
          function() { return FileTraversal.stuff; },
          // This is the change listener, called when the value returned from the above function changes (but not working) :(
          function(newValue, oldValue) {
            if ( newValue !== oldValue ) {

              console.log(newValue);
            } else {
              console.log('nope');
            }
          }
      );
    }
  }
})();

Service:

(function () {
  "use strict";
  var fs = require('fs');
  var glob = require("glob");
  angular
      .module('divkick.services', [])
      .factory('FileTraversal', FileTraversal);


  /* @ngInject */
  function FileTraversal() {
    var service = {
      stuff: [],
      check: check
    };

    return service;

    ////////////////

    function check(filePath) {
      glob(filePath + '/**/*{.png,.jpg,.gif}', function (er, files) {
        if(er) return er;
        service.stuff = files;
      });
    }
  }
})();

I tried watchcollection like this:

$scope.$watchCollection(

          function() { return FileTraversal.stuff; },
          function(newValue, oldValue) {
            if ( newValue !== oldValue ) {
              // Only increment the counter if the value changed
              console.log(newValue);
            } else {
              console.log('nope');
            }
          }
      );

Upvotes: 0

Views: 69

Answers (2)

Anik Islam Abhi
Anik Islam Abhi

Reputation: 25352

Try watch collection instead of watch

$scope.$watchCollection(function () {
    return FileTraversal.stuff;
}, function (newValue, oldValue) {
    console.log("check")
    if (newValue !== oldValue) {

        console.log(newValue);
    } else {
        console.log('nope');
    }
});

N:B:

you didn't add ng-controller in view in your fiddle . may be that's a typo

JSFIDDLE

Upvotes: 1

Adam Zerner
Adam Zerner

Reputation: 19238

The problem is that you're using $watch instead of $watchCollection.

$watch is currently looking at a property that points to an array. Even if the array changes, the property that points to the array doesn't change - the pointer is the same. So $watch doesn't detect this.

If you use $watchCollection, angular will know to examine the collection to see if any of the elements in the collection have changed.

See The Three Watch Depths of AngularJS for more info.

Plunker Demo

angular
  .module('app', [])
  .controller('MainController', MainController)
  .factory('Factory', Factory)
;

function MainController(Factory, $scope) {
  var main = this;
  main.factory = Factory;
  main.addToArray = function() {
    main.factory.array.push('added');
  };
  
  // watching primitive
  $scope.$watch(function() {
    return Factory.primitive;
  }, function(newVal, oldVal) {
    if (newVal === oldVal) {
      console.log('primitive unchanged');
    }
    else {
      console.log('primitive changed');
    }
  });
  
  // watching array
  $scope.$watchCollection(function() {
    return Factory.array;
  }, function(newVal, oldVal) {
    if (newVal === oldVal) {
      console.log('array unchanged');
    }
    else {
      console.log('array changed');
    }
  });
}

function Factory() {
  return {
    primitive: 'foo',
    array: []
  };
}
<!DOCTYPE html>
<html ng-app="app">

  <head>
    <script data-require="[email protected]" data-semver="1.4.6" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.min.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
    <div ng-controller="MainController as main">
      <input ng-model="main.factory.primitive" />
      <button ng-click='main.addToArray()'>Add to array</button>
    </div>
  </body>

</html>

Upvotes: 0

Related Questions