$watch inside a service?
Asked Answered
N

4

61

Is it possible to set up a $watch on an array of objects inside a service (I'd like the $watch declaration itself to be inside the service)?

Nedi answered 23/7, 2013 at 9:38 Comment(0)
S
122

You can add any expression to the set of watches by injecting the $rootScope and using a function a first argument to the $watch method. Pseudo-code:

$rootScope.$watch(function() {
  return //value to be watched;
}, function watchCallback(newValue, oldValue) {
  //react on value change here
});

Don't forget about the third, Boolean argument to the $watch method. You need to specify true if you want to deep-watch an entire object for changes.

Soak answered 23/7, 2013 at 9:52 Comment(6)
if you don't mind, i would like you to show us how you reference the returning service value from the first function. I tried my best, but didn't succeed watching for changes. The same value was constantly being returned, over and over.Brolly
This doesn't help as it's watching the rootScope for the entire application.Rostellum
@Rostellum I don't think your comment is accurate. What gets watched here is a result of a first function call. There is nothing like "watching the rootScope for the entire application", there is no such concept at play here.Soak
I've created a jsfiddle example to demonstrate how this works: jsfiddle.net/gvJdn/158Serialize
Is this really the best option? I think this encourages a 'add the rootScope' everywhere which i (when I was a beginner was guilty of) injecting rootScope like I was adding semi colons. It's tough :DAnthelmintic
@Serialize : this is working only when the array is being modified through push method. If I directly initialize the array with another, then it isn't working. (the ng-repeat is not reflecting, but console.log shows modified array) Any specific reason ?Sloth
R
15

services by their very nature are like classes and house methods. You can set up a method which you call from your controller who's argument is the scope and applies a watch expression:

app.service('myservice',function(){
    this.listen = function($scope) {
        $scope.$watch(function(){return someScopeValue},function(){
           //$scope.dosomestuff();
        });
    }
});

//your controller

function myCtrl($scope,myservice) {
    $scope.listen = function() {
        myservice.listen($scope);
    }

    //call your method
    $scope.listen();
}

update: If you are trying to watch a private local variable inside of a service, see the accepted answer using $rootScope. If you are trying to $watch a $scope variable within a local scope than the above is your best bet. They are achieving two very different things.

Rostellum answered 25/3, 2014 at 1:13 Comment(1)
That may seem like a dumb question but why are you putting myservice.listen($scope) inside a function? Is it for easily reusing it throughout the code or is there some important reason? Besides, if I put the $watch in a factory, will it still work?Neediness
R
0

Can you clarify what you want to achieve? As I understand, you are getting an array of objects from the server as your model. Then it's not clear:

  1. Do you want to verify if some objects in the array have been changed (maybe since the last call to the server in case you do recurrent pooling)?
  2. Do you want to verify if some objects in the array have been changed by the user via some front-end control elements?

In case 1, you don't really need to use $watch (or rather $watchCollection) but you should iterate through the array you received from the server and verify the changes (the same what $watchColleciton would do). In case the object is different to the one you currently hold - you call object.$save() on this element.

In case 2, use $watchCollection() on the $scope passed as a parameter to your service function or use $rootScope if you hold your array in the $rootScope.

I can provide you with real code if you would clarify the scenarios.

Roop answered 25/3, 2014 at 1:51 Comment(0)
R
0

Hey just had a situation where I had a pretty big watch setup on 2 different controllers.

As I didn't want to duplicate any code I just defined to function in my service :

angular
    .module('invoice')
    .factory('invoiceState', function () {
        var invoice = {
            client_info: {
                lastname: "",
                firstname: "",
                email: "",
                phone: "",
                id_client: "",
                ... 
             }
        }
        invoice.watchAccommodation = function (newVal, oldVal) {
            var accommodation = newVal;
            //Whatever you would normally have in your watch function
        }

Then I can just call this function in the watch of any controller into which I injected the service

function NewInvoiceCtrl(invoiceState) {
    $scope.$watch('invoice.accommodation',invoiceState.watchAccommodation, true);
}

I do not have much experience with AngularJS so I can't really go into the performance aspects of this approach but it works ;)

Rackrent answered 27/2, 2018 at 16:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.