Search code examples
angularjsangularjs-scopeangularjs-watch

Can you reuse $scope.$watch


I found some code I want to copy / paste and use in two controllers. It watches something.

$scope.$watch('thing', function (thing) {
  // do cool stuff with thing
}

Instead of copy/paste, I'd like to put it in a service and use the service from both controllers sortof like this:

angular.module('myApp')
.factory('CoolService',
  function () {
    $scope.$watch('thing', function (thing) {
      // do cool stuff with thing
    }
}

Now if I do this, it won't know what $scope is, right? (According to some reading, it won't let me do that anyway.)

Nevertheless, I'd like to say, If you have this service, you get this watch.

There's a hint I can do this: Passing current scope to an AngularJS Service

So I took his example, fixed it, and scope.watch works in there, but now I can't set other scope variables inside the watch. I just don't know enough javascript to do it, but I'm close. I really think it will work with the right syntax...

angular.module('blah', []);

angular.module('blah').factory('BlahService', function() {
  //constructor
  function BlahService(scope) {
      this._scope = scope;
      this.myFunc = function(){
        this._scope.otherVar = this._scope.someVar;
      };
      this._scope.$watch('someVar', function(someVar) {
        // do cool stuff with thing
        _scope.otherVar = this._scope.someVar; // undefined
        this._scope.otherVar = this._scope.someVar; // undefined
        this.myFunc(); // undefined
        BlahService.prototype._someFunction(); // works, but...
        return someVar;
      });

    }

    //wherever you'd reference the scope
  BlahService.prototype._someFunction = function() {
    if (this._scope['someVar'] == 1) // undefined
      this._scope['someVar']++;
  }

  return BlahService;

});

angular.module('blah').controller('BlahCtrl', function($scope, BlahService) {
  $scope.someVar = 4;
  $scope.BlahService = new BlahService($scope);
});

angular.module('blah').controller('Blah2Ctrl', function($scope, BlahService) {
  $scope.someVar = 6;
  $scope.BlahService = new BlahService($scope);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="blah">
  <body>
    <div ng-controller="BlahCtrl">
      1a. <input ng-model="someVar">
      1b. <input ng-model="otherVar">
    </div>
<div ng-controller="Blah2Ctrl">
      2. <input ng-model="someVar">
  2b. <input ng-model="otherVar">
    </div>
  </body>
</html>

The key feature that this snippet has is that the scopes are different scopes. It doesn't act like a singleton.


Solution

  • This did it, and it allows me to set other members of the scope from within the watch:

    angular.module('blah', []);
    
    angular.module('blah').factory('BlahService', function() {
      //constructor
      function BlahService(scope) {
        this._scope = scope;
        this.myFunc = function() {
          this._scope.otherVar = this._scope.someVar;
        };
        this._scope.$watch('someVar', function(newValue, oldValue, scope) {
          // do cool stuff with thing
          scope.otherVar = Number(scope.someVar) + 1;
          return newValue;
        });
      }
      return BlahService;
    });
    
    angular.module('blah').controller('BlahCtrl', function($scope, BlahService) {
      $scope.someVar = 4;
      $scope.BlahService = new BlahService($scope);
    });
    
    angular.module('blah').controller('Blah2Ctrl', function($scope, BlahService) {
      $scope.someVar = 6;
      $scope.BlahService = new BlahService($scope);
    });
    <html ng-app="blah">
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
      <body>
        <div ng-controller="BlahCtrl">
          1a. <input ng-model="someVar">
          1b. <input ng-model="otherVar">
        </div>
    <div ng-controller="Blah2Ctrl">
          2. <input ng-model="someVar">
      2b. <input ng-model="otherVar">
        </div>
      </body>
    </html>