I have a service, say:
factory(\'aService\', [\'$rootScope\', \'$resource\', function ($rootScope, $resource) {
var service = {
foo: []
};
return
I have written two simple utility services that help me track service properties changes.
If you want to skip the long explanation, you can go strait to jsfiddle
mod.service('WatchObj', ['$rootScope', WatchObjService]);
function WatchObjService($rootScope) {
// returns watch function
// obj: the object to watch for
// fields: the array of fields to watch
// target: where to assign changes (usually it's $scope or controller instance)
// $scope: optional, if not provided $rootScope is use
return function watch_obj(obj, fields, target, $scope) {
$scope = $scope || $rootScope;
//initialize watches and create an array of "unwatch functions"
var watched = fields.map(function(field) {
return $scope.$watch(
function() {
return obj[field];
},
function(new_val) {
target[field] = new_val;
}
);
});
//unregister function will unregister all our watches
var unregister = function unregister_watch_obj() {
watched.map(function(unregister) {
unregister();
});
};
//automatically unregister when scope is destroyed
$scope.$on('$destroy', unregister);
return unregister;
};
}
This service is used in the controller in the following way: Suppose you have a service "testService" with the properties 'prop1', 'prop2', 'prop3'. You want to watch and assign to scope 'prop1' and 'prop2'. With the watch service it will look like that:
app.controller('TestWatch', ['$scope', 'TestService', 'WatchObj', TestWatchCtrl]);
function TestWatchCtrl($scope, testService, watch) {
$scope.prop1 = testService.prop1;
$scope.prop2 = testService.prop2;
$scope.prop3 = testService.prop3;
watch(testService, ['prop1', 'prop2'], $scope, $scope);
}
mod.service('apply', ['$timeout', ApplyService]);
function ApplyService($timeout) {
return function apply() {
$timeout(function() {});
};
}
I would trigger it in the end of my async code to trigger the $digest loop. Like that:
app.service('TestService', ['apply', TestService]);
function TestService(apply) {
this.apply = apply;
}
TestService.prototype.test3 = function() {
setTimeout(function() {
this.prop1 = 'changed_test_2';
this.prop2 = 'changed2_test_2';
this.prop3 = 'changed3_test_2';
this.apply(); //trigger $digest loop
}.bind(this));
}
So, all of that together will look like that (you can run it or open fiddle):
// TEST app code
var app = angular.module('app', ['watch_utils']);
app.controller('TestWatch', ['$scope', 'TestService', 'WatchObj', TestWatchCtrl]);
function TestWatchCtrl($scope, testService, watch) {
$scope.prop1 = testService.prop1;
$scope.prop2 = testService.prop2;
$scope.prop3 = testService.prop3;
watch(testService, ['prop1', 'prop2'], $scope, $scope);
$scope.test1 = function() {
testService.test1();
};
$scope.test2 = function() {
testService.test2();
};
$scope.test3 = function() {
testService.test3();
};
}
app.service('TestService', ['apply', TestService]);
function TestService(apply) {
this.apply = apply;
this.reset();
}
TestService.prototype.reset = function() {
this.prop1 = 'unchenged';
this.prop2 = 'unchenged2';
this.prop3 = 'unchenged3';
}
TestService.prototype.test1 = function() {
this.prop1 = 'changed_test_1';
this.prop2 = 'changed2_test_1';
this.prop3 = 'changed3_test_1';
}
TestService.prototype.test2 = function() {
setTimeout(function() {
this.prop1 = 'changed_test_2';
this.prop2 = 'changed2_test_2';
this.prop3 = 'changed3_test_2';
}.bind(this));
}
TestService.prototype.test3 = function() {
setTimeout(function() {
this.prop1 = 'changed_test_2';
this.prop2 = 'changed2_test_2';
this.prop3 = 'changed3_test_2';
this.apply();
}.bind(this));
}
//END TEST APP CODE
//WATCH UTILS
var mod = angular.module('watch_utils', []);
mod.service('apply', ['$timeout', ApplyService]);
function ApplyService($timeout) {
return function apply() {
$timeout(function() {});
};
}
mod.service('WatchObj', ['$rootScope', WatchObjService]);
function WatchObjService($rootScope) {
// target not always equals $scope, for example when using bindToController syntax in
//directives
return function watch_obj(obj, fields, target, $scope) {
// if $scope is not provided, $rootScope is used
$scope = $scope || $rootScope;
var watched = fields.map(function(field) {
return $scope.$watch(
function() {
return obj[field];
},
function(new_val) {
target[field] = new_val;
}
);
});
var unregister = function unregister_watch_obj() {
watched.map(function(unregister) {
unregister();
});
};
$scope.$on('$destroy', unregister);
return unregister;
};
}
prop1: {{prop1}}
prop2: {{prop2}}
prop3 (unwatched): {{prop3}}