本文翻译自:AngularJS : How to watch service variables?
I have a service, say: 我有服务,说:
factory('aService', ['$rootScope', '$resource', function ($rootScope, $resource) {
var service = {
foo: []
};
return service;
}]);
And I would like to use foo
to control a list that is rendered in HTML: 我想使用foo
来控制以HTML格式呈现的列表:
<div ng-controller="FooCtrl">
<div ng-repeat="item in foo">{{ item }}</div>
</div>
In order for the controller to detect when aService.foo
is updated I have cobbled together this pattern where I add aService to the controller's $scope
and then use $scope.$watch()
: 为了让控制器检测aService.foo
更新,我将这个模式拼凑在一起,我将aService添加到控制器的$scope
,然后使用$scope.$watch()
:
function FooCtrl($scope, aService) {
$scope.aService = aService;
$scope.foo = aService.foo;
$scope.$watch('aService.foo', function (newVal, oldVal, scope) {
if(newVal) {
scope.foo = newVal;
}
});
}
This feels long-handed, and I've been repeating it in every controller that uses the service's variables. 这感觉很长,我一直在每个使用服务变量的控制器中重复它。 Is there a better way to accomplish watching shared variables? 有没有更好的方法来完成观察共享变量?
#1楼
参考:https://stackoom.com/question/qlna/AngularJS-如何观察服务变量
#2楼
As far as I can tell, you dont have to do something as elaborate as that. 据我所知,你不必做那样详细的事情。 You have already assigned foo from the service to your scope and since foo is an array ( and in turn an object it is assigned by reference! ). 您已经将foo从服务分配给范围,因为foo是一个数组(反过来它是一个通过引用分配的对象!)。 So, all that you need to do is something like this : 所以,你需要做的就是这样:
function FooCtrl($scope, aService) {
$scope.foo = aService.foo;
}
If some, other variable in this same Ctrl is dependant on foo changing then yes, you would need a watch to observe foo and make changes to that variable. 如果有的话,同一个Ctrl中的其他变量依赖于foo更改然后是,你需要一个手表来观察foo并对该变量进行更改。 But as long as it is a simple reference watching is unnecessary. 但只要它是一个简单的参考,观看是不必要的。 Hope this helps. 希望这可以帮助。
#3楼
A wee bit ugly, but I've added registration of scope variables to my service for a toggle: 有点难看,但我已经将范围变量的注册添加到我的服务中以进行切换:
myApp.service('myService', function() {
var self = this;
self.value = false;
self.c2 = function(){};
self.callback = function(){
self.value = !self.value;
self.c2();
};
self.on = function(){
return self.value;
};
self.register = function(obj, key){
self.c2 = function(){
obj[key] = self.value;
obj.$apply();
}
};
return this;
});
And then in the controller: 然后在控制器中:
function MyCtrl($scope, myService) {
$scope.name = 'Superhero';
$scope.myVar = false;
myService.register($scope, 'myVar');
}
#4楼
You can always use the good old observer pattern if you want to avoid the tyranny and overhead of $watch
. 如果你想避免$watch
的暴政和开销,你总是可以使用好的旧观察者模式。
In the service: 在服务中:
factory('aService', function() {
var observerCallbacks = [];
//register an observer
this.registerObserverCallback = function(callback){
observerCallbacks.push(callback);
};
//call this when you know 'foo' has been changed
var notifyObservers = function(){
angular.forEach(observerCallbacks, function(callback){
callback();
});
};
//example of when you may want to notify observers
this.foo = someNgResource.query().$then(function(){
notifyObservers();
});
});
And in the controller: 在控制器中:
function FooCtrl($scope, aService){
var updateFoo = function(){
$scope.foo = aService.foo;
};
aService.registerObserverCallback(updateFoo);
//service now in control of updating foo
};
#5楼
Building on dtheodor's answer you could use something similar to the below to ensure that you don't forget to unregister the callback... Some may object to passing the $scope
to a service though. 基于dtheodor的答案,您可以使用类似于下面的内容来确保您不会忘记取消注册回调...有些人可能会反对将$scope
传递给服务。
factory('aService', function() {
var observerCallbacks = [];
/**
* Registers a function that will be called when
* any modifications are made.
*
* For convenience the callback is called immediately after registering
* which can be prevented with `preventImmediate` param.
*
* Will also automatically unregister the callback upon scope destory.
*/
this.registerObserver = function($scope, cb, preventImmediate){
observerCallbacks.push(cb);
if (preventImmediate !== true) {
cb();
}
$scope.$on('$destroy', function () {
observerCallbacks.remove(cb);
});
};
function notifyObservers() {
observerCallbacks.forEach(function (cb) {
cb();
});
};
this.foo = someNgResource.query().$then(function(){
notifyObservers();
});
});
Array.remove is an extension method which looks like this: Array.remove是一个扩展方法,如下所示:
/**
* Removes the given item the current array.
*
* @param {Object} item The item to remove.
* @return {Boolean} True if the item is removed.
*/
Array.prototype.remove = function (item /*, thisp */) {
var idx = this.indexOf(item);
if (idx > -1) {
this.splice(idx, 1);
return true;
}
return false;
};
#6楼
Here's my generic approach. 这是我的通用方法。
mainApp.service('aService',[function(){
var self = this;
var callbacks = {};
this.foo = '';
this.watch = function(variable, callback) {
if (typeof(self[variable]) !== 'undefined') {
if (!callbacks[variable]) {
callbacks[variable] = [];
}
callbacks[variable].push(callback);
}
}
this.notifyWatchersOn = function(variable) {
if (!self[variable]) return;
if (!callbacks[variable]) return;
angular.forEach(callbacks[variable], function(callback, key){
callback(self[variable]);
});
}
this.changeFoo = function(newValue) {
self.foo = newValue;
self.notifyWatchersOn('foo');
}
}]);
In Your Controller 在您的控制器中
function FooCtrl($scope, aService) {
$scope.foo;
$scope._initWatchers = function() {
aService.watch('foo', $scope._onFooChange);
}
$scope._onFooChange = function(newValue) {
$scope.foo = newValue;
}
$scope._initWatchers();
}
FooCtrl.$inject = ['$scope', 'aService'];