如何跟踪AngularJS中的事件?

时间:2014-06-18 21:27:47

标签: angularjs

我有一个包含项目的应用,您可以执行添加新项目,更新项目文本,将项目移动到其他文件夹等操作。

我有一个items工厂,它将所有项目保存为数组中的普通对象,并且工厂返回一个具有各种方法的单例,例如get()set()等。

要在问题中添加一些上下文,我也在使用Node.js和MongoDB。

无论如何,由于我拥有的所有工厂,如itemsfolders,以及所有不同视图的控制器,我都非常依赖于事件。举一些例子:

// items factory
update: function(params) {
    // add to database, then...
    .then(function() {
        $rootScope.$emit('itemCreated');
    });
}

// items controller

// I need to refresh the items list in the scope
$rootScope.$on('itemCreated', function() { // when an item is added to the database
    $scope.items = items.getAll(); // retrieve all items from the items factory
});

这些是他们自己的"子集"事件,因为他们都属于" CRUD"对项目的操作

但是,我还有其他的事件。例如,我有一个侦听任何请求和响应的拦截器。我有一个使用指令的加载小部件(旋转轮的图像)。该指令将在请求开始时显示加载小部件,并在请求结束时隐藏加载小部件。这也是基于事件的。

// on request
$rootScope.$emit(_START_REQUEST_);

// on any response
$rootScope.$emit(_END_REQUEST_);

我试图"模块化"这些请求和响应事件只需简单地使它们成为常量。

.constant('_START_REQUEST_', '_START_REQUEST_');

我正在努力找到一个解决方案,以便"模块化"我的所有其他事件,例如在项目的CRUD操作上发出的事件。我有一个想法是定义items工厂内的所有项目CRUD事件:

events: {
    update: 'itemUpdate',
    create: 'itemCreated'
    // etc.
}

然后,我可以简单地将我的items工厂注入控制器,并像这样引用事件:

$rootScope.$on(items.events.update, function() {});

我还考虑将所有事件定义为我的应用中的常量,无论它们是拦截器事件还是项目事件。但是,似乎这个解决方案直接将项目事件耦合到模块本身,而不是items工厂,这是我认为他们属于"的地方。

基本上,问题是现在所有的事件定义似乎都散布在各地。我的问题是:您建议在AngularJS中模块化和定义事件的模式或最佳实践是什么?

4 个答案:

答案 0 :(得分:4)

我同意这些项目事件应该属于事件源。您可以在项目工厂中实现observer pattern,以隐藏事件侦听器对$rootScope的依赖关系。这样,事件键本身就是项目工厂的私有细节,并且通过为其调用专用函数来明确对事件的订阅。这种方法使您的代码更加独立于$rootScope并且比事件名称约定更容易维护(考虑使用搜索特定事件订阅方法与$rootScope.$emit / $on的用法):

angular.module('events', [])

.service('items', ['$rootScope', function($rootScope) {
  var createdEventKey = 'item.created';
    
  return {
      create: function () {
          $rootScope.$emit(createdEventKey, {"name": "aItemName"});
      },
      
      onCreated: function(callback, scope) {
          var unsubscribeFunction = $rootScope.$on(createdEventKey, function(event, payload) {
              callback(payload);
          });
          
          // allow to unsubscribe automatically on scope destroy to prevent memory leaks 
          if (scope) {
            scope.$on("$destroy", unsubscribeFunction);
          }
          
          return unsubscribeFunction;
      }   
  }
}])

.controller('TestController', function($scope, items) {
    items.onCreated(function (item) {
        console.log("Created: " + item.name);
    }, $scope);
});

完整示例:http://jsfiddle.net/8LtyB/32/

答案 1 :(得分:0)

如果你想要的是一种创建单独对象以包含事件名称的方法,为什么不使用服务?

myApp.service('itemEvents', function () {
  var events = {
    update: 'itemupdate',
    create: 'itemcreate',
    ...
  };
  return events;
});

这基本上是您之前建议使用工厂包含事件定义时所拥有的,除了服务是单个对象实例,并在模块启动时实例化。相反,工厂在注入控制器时会创建一个新实例。 (Here's a good SO post on the difference between services and factories

您可以将此服务注入您的控制器或指令:

myApp.controller('ItemController', function ($scope, itemEvents) {
  $scope.on(itemEvents.update, function () { /* something interesting */ });
});

这为您提供了一个集中事件名称定义的好地方。作为旁注,有些人坚持在定义事件名称时使用全部小写的惯例(所以itemupdate而不是itemUpdate)。希望这有帮助!

答案 2 :(得分:0)

您可以使用以下内容:

app.config(function($provide) {
    $provide.decorator("$rootScope", function($delegate) {
        var Scope = $delegate.constructor;
        var origBroadcast = Scope.prototype.$broadcast;
        var origEmit = Scope.prototype.$emit;

        Scope.prototype.$broadcast = function() {
            console.log("$broadcast was called on $scope " + Scope.$id + " with arguments:",
                arguments);
            return origBroadcast.apply(this, arguments);
        };
        Scope.prototype.$emit = function() {
            console.log("$emit was called on $scope " + Scope.$id + " with arguments:",
                arguments);
            return origEmit.apply(this, arguments);
        };
        return $delegate;
    });
})

示例:http://plnkr.co/edit/cn3MZynbpTYIcKUWmsBi?p=preview

src:https://github.com/angular/angular.js/issues/6043

答案 3 :(得分:-1)

假设这些$ scope。$ emit的作用类似于jquery事件我建议你将你的emits命名为泛型,例如在你的数据库更新中,只需这样做:

$rootScope.$emit('Created')

然后在你的物品控制器中执行此操作:

$rootScope.$on('Created.item', function() { // when an item is added to the database
    $scope.items = items.getAll(); // retrieve all items from the items factory
});

然后您可以连接到任何控制器中创建的事件,其名称是通用的。 .item应该添加命名空间。如果您使项目控制器中的所有事件都具有.item名称空间,则应该能够执行

$rootScope.$off('item')

这将清除内存泄漏