如何在AngularJs服务中创建自定义事件

37

我正在开发一个AngularJs项目。我有一个服务,用于设置和删除一些按钮上的事件。另外一个服务利用了这个服务,但我不希望直接与按钮交互。然而,当按钮被单击时,我想要将点击事件过滤到第一个服务中,并在第二个服务中处理。由于我不希望第二个服务知道这些按钮的存在,所以我打算在第一个服务中创建一个自定义事件。如何创建自定义事件并在按钮被点击时触发它?

谢谢提前。


也许观察者模式可以为这个问题带来其他解决方案 https://scotch.io/bar-talk/4-javascript-design-patterns-you-should-know#observer-design-pattern - William Ardila
3个回答

85
如果您想在服务/指令之间发送事件,请使用broadcast
$rootScope.$broadcast('buttonPressedEvent');

然后像这样接收它:

$rootScope.$on('buttonPressedEvent', function () {
             //do stuff
        })

嘿,非常感谢。但是服务本身是否可以触发事件,以便于第二个服务必须引用第一个服务来处理它?例如:在service2内部,会有一行代码:service1.eventfired = do_something()。 - rmg.n3t
是的,那是我回答中的最后一部分。在您的第二个服务中,您需要添加该代码,只需将$scope.$on更改为$rootScope.$on即可。 - Chancho
这比我考虑的要干净得多。为你欢呼,为$rootscope.$broadcast()欢呼! - rinogo

35

任何基于全局作用域的集成都会损害命名空间,并可能导致中大型应用程序发生可怕的副作用。我建议使用代理服务,这个解决方案略微复杂但绝对更为清晰。

1. 创建代理服务

angular.module('app').service('userClickingHelper', [
  function() {
    var listeners = [];
    return {
      click: function(args) {
        listeners.forEach(function(cb) {
          cb(args);
        });
      },
      register: function(callback) {
        listeners.push(callback);
      }
    };
  }
]);

2. 创建一个按钮指令,该指令使用userClickingHelper作为依赖项。

angular.module('app').directive('onDistributedClick', ['userClickingHelper', function (userClickingHelper) {
    return {
        restrict: 'A',
        link: function (scope, element) {
            element.on('click', userClickingHelper.click);
        }
    };
}]);

3. 使用代理注册您的非关联服务。

angular.module('app').service('myUnrelatedService', [function (userClickingHelper) {
    userClickingHelper.register(function(){
        console.log("The button is clicked somewhere");
    });
}]);

这个结果是一个独立的事件分配范围,然而指令和服务彼此不知道。这也更容易进行单元测试。

我正在使用类似的解决方案来全局访问“加载”模态框,以便我可以抽象出控制器/服务告诉“用户现在不应该点击或执行任何操作”的方式。


这个答案提供了在实现监听器/处理程序时的一般良好实践,而Chancho的答案更加适用于OP的用例。 - Yatit Thakker
我喜欢这种方法,因为我们可以从可信源挂钩通知,或者我们知道它应该来自哪里。如果我们使用$rootScope.$broadcast,那么在复杂的应用程序中,有很高的机会有人会重叠在同一个事件上广播他们的数据。 - Aamol

8
您可以在您的元素上使用以下代码来创建和触发事件:
ng-click="$emit('customEvent')"

然后在您的控制器中,您可以使用:
$rootScope.$on('customEvent', function(){
    // do something
})

演示


我希望两个“服务”进行通信,而不是视图和控制器。然而,在阅读了您的答案后,我有了一种顿悟。我可以在这两个服务之间建立双向通信。当服务1捕获按钮点击事件时,它可以调用服务2中所需的方法来进行所需的处理。这似乎是实现我所需的简单方法。非常感谢! - rmg.n3t
算了吧!我会把这两个服务紧密耦合在一起的!我将使用另一个用户指出的$rootScope.$on()和$rootScope.$broadcast()方法。再次感谢!(如果你想知道,是的,我是个新手)! - rmg.n3t
它实际上完全相同,我的演示具有广播和发射功能,我在演示中使用了发射,我的方法是直接从DOM发射,而不是在控制器中添加更多逻辑。 - Jonathan de M.
这个回答需要更多的赞!对于我的需求,$rootScope.$broadcast() 更合适,但我能理解,在某些情况下,使用 ng-click="$emit('customEvent')" 会更好。 - rinogo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接