我有一个服务,说:

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;
    }
  });
}

这感觉有点冗长,我一直在每个使用服务变量的控制器中重复这一点。有没有更好的方法来监视共享变量?


当前回答

你可以在$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");​

其他回答

在这样的场景中,多个/未知对象可能对更改感兴趣,请使用$rootScope。$broadcast来自正在更改的项。

而不是创建自己的侦听器注册表(必须在各种$ destroyed上进行清理),您应该能够从相关服务进行$broadcast。

您仍然必须在每个侦听器中编写$on处理程序,但该模式与多次调用$digest分离,从而避免了长时间运行的监视器的风险。

通过这种方式,侦听器也可以从DOM和/或不同的子作用域进出,而不需要服务更改其行为。

**更新:示例**

Broadcasts would make the most sense in "global" services that could impact countless other things in your app. A good example is a User service where there are a number of events that could take place such as login, logout, update, idle, etc. I believe this is where broadcasts make the most sense because any scope can listen for an event, without even injecting the service, and it doesn't need to evaluate any expressions or cache results to inspect for changes. It just fires and forgets (so make sure it's a fire-and-forget notification, not something that requires action)

.factory('UserService', [ '$rootScope', function($rootScope) {
   var service = <whatever you do for the object>

   service.save = function(data) {
     .. validate data and update model ..
     // notify listeners and provide the data that changed [optional]
     $rootScope.$broadcast('user:updated',data);
   }

   // alternatively, create a callback function and $broadcast from there if making an ajax call

   return service;
}]);

当save()函数完成并且数据有效时,上面的服务将向每个作用域广播一条消息。或者,如果它是$resource或ajax提交,则将广播调用移动到回调中,以便在服务器响应时触发它。广播特别适合这种模式,因为每个侦听器只需要等待事件,而不需要检查每个$摘要上的作用域。监听器看起来是这样的:

.controller('UserCtrl', [ 'UserService', '$scope', function(UserService, $scope) {

  var user = UserService.getUser();

  // if you don't want to expose the actual object in your scope you could expose just the values, or derive a value for your purposes
   $scope.name = user.firstname + ' ' +user.lastname;

   $scope.$on('user:updated', function(event,data) {
     // you could inspect the data to see if what you care about changed, or just update your own scope
     $scope.name = user.firstname + ' ' + user.lastname;
   });

   // different event names let you group your code and logic by what happened
   $scope.$on('user:logout', function(event,data) {
     .. do something differently entirely ..
   });

 }]);

这样做的好处之一是不需要多块手表。如果您像上面的例子一样组合字段或派生值,则必须同时注意firstname和lastname属性。只有当用户对象在更新时被替换时,观察getUser()函数才会工作,如果用户对象仅仅更新了它的属性,它就不会触发。在这种情况下,你必须进行深度观察,这是更密集的。

$broadcast将消息从它所调用的作用域发送到任何子作用域。因此从$rootScope调用它将在每个作用域上触发。例如,如果从控制器的作用域执行$broadcast,它只会在继承自控制器作用域的作用域中触发。$emit走向相反的方向,其行为类似于DOM事件,因为它在作用域链中冒泡。

请记住,在某些情况下,$broadcast很有意义,而在某些情况下,$watch是更好的选择——特别是在具有非常特定的watch表达式的孤立作用域中。

当我面对一个非常相似的问题时,我观察了一个作用域中的函数,并让函数返回服务变量。我已经创建了一个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++;
    });
});

有点丑,但我已经为切换添加了范围变量的注册到我的服务:

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');
}

我在这里看到过一些可怕的观察者模式,它们会在大型应用程序上导致内存泄漏。

我可能会来晚一点,但就是这么简单。

如果你想查看数组推送之类的东西,可以使用watch函数查看引用更改(基本类型):

someArray.push(someObj);someArray = someArray.splice(0);

这将从任何地方更新引用和手表。包括一个服务getter方法。 任何原语都将被自动更新。

我使用类似的方法@dtheodot,但使用角承诺而不是传递回调

app.service('myService', function($q) {
    var self = this,
        defer = $q.defer();

    this.foo = 0;

    this.observeFoo = function() {
        return defer.promise;
    }

    this.setFoo = function(foo) {
        self.foo = foo;
        defer.notify(self.foo);
    }
})

然后只要使用myService.setFoo(foo)方法来更新服务上的foo。在你的控制器中,你可以这样使用它:

myService.observeFoo().then(null, null, function(foo){
    $scope.foo = foo;
})

then的前两个参数是成功和错误回调,第三个参数是通知回调。

$q的参考。