我有一个服务,说:
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;
}
});
}
这感觉有点冗长,我一直在每个使用服务变量的控制器中重复这一点。有没有更好的方法来监视共享变量?
当我面对一个非常相似的问题时,我观察了一个作用域中的函数,并让函数返回服务变量。我已经创建了一个js小提琴。您可以在下面找到代码。
var myApp = angular.module("myApp",[]);
myApp.factory("randomService", function($timeout){
var retValue = {};
var data = 0;
retValue.startService = function(){
updateData();
}
retValue.getData = function(){
return data;
}
function updateData(){
$timeout(function(){
data = Math.floor(Math.random() * 100);
updateData()
}, 500);
}
return retValue;
});
myApp.controller("myController", function($scope, randomService){
$scope.data = 0;
$scope.dataUpdated = 0;
$scope.watchCalled = 0;
randomService.startService();
$scope.getRandomData = function(){
return randomService.getData();
}
$scope.$watch("getRandomData()", function(newValue, oldValue){
if(oldValue != newValue){
$scope.data = newValue;
$scope.dataUpdated++;
}
$scope.watchCalled++;
});
});
没有手表或观察者回调(http://jsfiddle.net/zymotik/853wvv7s/):
JavaScript:
angular.module("Demo", [])
.factory("DemoService", function($timeout) {
function DemoService() {
var self = this;
self.name = "Demo Service";
self.count = 0;
self.counter = function(){
self.count++;
$timeout(self.counter, 1000);
}
self.addOneHundred = function(){
self.count+=100;
}
self.counter();
}
return new DemoService();
})
.controller("DemoController", function($scope, DemoService) {
$scope.service = DemoService;
$scope.minusOneHundred = function() {
DemoService.count -= 100;
}
});
HTML
<div ng-app="Demo" ng-controller="DemoController">
<div>
<h4>{{service.name}}</h4>
<p>Count: {{service.count}}</p>
</div>
</div>
这个JavaScript在我们从服务返回一个对象而不是一个值时工作。当一个JavaScript对象从服务返回时,Angular会向它的所有属性中添加手表。
还要注意,我使用'var self = this',因为我需要在$timeout执行时保持对原始对象的引用,否则'this'将引用窗口对象。
你可以在$rootScope中插入服务,然后观察:
myApp.run(function($rootScope, aService){
$rootScope.aService = aService;
$rootScope.$watch('aService', function(){
alert('Watch');
}, true);
});
在你的控制器中:
myApp.controller('main', function($scope){
$scope.aService.foo = 'change';
});
另一个选择是使用外部库,例如:https://github.com/melanke/Watch.JS
"发射架:IE +, f4 +,稳定基金5+
您可以观察一个、多个或所有对象属性的变化。
例子:
var ex3 = {
attr1: 0,
attr2: "initial value of attr2",
attr3: ["a", 3, null]
};
watch(ex3, function(){
alert("some attribute of ex3 changes!");
});
ex3.attr3.push("new value");
= = = =更新
在$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 + '!');
});
}]);
如果您想避免$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
};
有点丑,但我已经为切换添加了范围变量的注册到我的服务:
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;
});
然后在控制器中:
function MyCtrl($scope, myService) {
$scope.name = 'Superhero';
$scope.myVar = false;
myService.register($scope, 'myVar');
}