我有一个服务,说:
factory('aService', ['$rootScope', '$resource', function ($rootScope, $resource) {
var service = {
foo: []
};
return service;
}]);
我想用foo来控制一个在HTML中呈现的列表:
<div ng-controller="FooCtrl">
<div ng-repeat="item in foo">{{ item }}</div>
</div>
以便控制器检测aService。我已经拼凑了这个模式,其中我添加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;
}
});
}
这感觉有点冗长,我一直在每个使用服务变量的控制器中重复这一点。有没有更好的方法来监视共享变量?
= = = =更新
在$watch中非常简单。
笔在这里。
HTML:
<div class="container" data-ng-app="app">
<div class="well" data-ng-controller="FooCtrl">
<p><strong>FooController</strong></p>
<div class="row">
<div class="col-sm-6">
<p><a href="" ng-click="setItems([ { name: 'I am single item' } ])">Send one item</a></p>
<p><a href="" ng-click="setItems([ { name: 'Item 1 of 2' }, { name: 'Item 2 of 2' } ])">Send two items</a></p>
<p><a href="" ng-click="setItems([ { name: 'Item 1 of 3' }, { name: 'Item 2 of 3' }, { name: 'Item 3 of 3' } ])">Send three items</a></p>
</div>
<div class="col-sm-6">
<p><a href="" ng-click="setName('Sheldon')">Send name: Sheldon</a></p>
<p><a href="" ng-click="setName('Leonard')">Send name: Leonard</a></p>
<p><a href="" ng-click="setName('Penny')">Send name: Penny</a></p>
</div>
</div>
</div>
<div class="well" data-ng-controller="BarCtrl">
<p><strong>BarController</strong></p>
<p ng-if="name">Name is: {{ name }}</p>
<div ng-repeat="item in items">{{ item.name }}</div>
</div>
</div>
JavaScript:
var app = angular.module('app', []);
app.factory('PostmanService', function() {
var Postman = {};
Postman.set = function(key, val) {
Postman[key] = val;
};
Postman.get = function(key) {
return Postman[key];
};
Postman.watch = function($scope, key, onChange) {
return $scope.$watch(
// This function returns the value being watched. It is called for each turn of the $digest loop
function() {
return Postman.get(key);
},
// This is the change listener, called when the value returned from the above function changes
function(newValue, oldValue) {
if (newValue !== oldValue) {
// Only update if the value changed
$scope[key] = newValue;
// Run onChange if it is function
if (angular.isFunction(onChange)) {
onChange(newValue, oldValue);
}
}
}
);
};
return Postman;
});
app.controller('FooCtrl', ['$scope', 'PostmanService', function($scope, PostmanService) {
$scope.setItems = function(items) {
PostmanService.set('items', items);
};
$scope.setName = function(name) {
PostmanService.set('name', name);
};
}]);
app.controller('BarCtrl', ['$scope', 'PostmanService', function($scope, PostmanService) {
$scope.items = [];
$scope.name = '';
PostmanService.watch($scope, 'items');
PostmanService.watch($scope, 'name', function(newVal, oldVal) {
alert('Hi, ' + newVal + '!');
});
}]);
基于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();
});
});
数组中。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;
};
如果您想避免$watch的暴政和开销,您总是可以使用老式的观察器模式。
在服务中:
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();
});
});
在控制器中:
function FooCtrl($scope, aService){
var updateFoo = function(){
$scope.foo = aService.foo;
};
aService.registerObserverCallback(updateFoo);
//service now in control of updating foo
};