如何在服务中封装单个事件和时间事件? [英] How to encapsulate single and temporal events in a service?
问题描述
我正在尝试将事件封装在服务中,以便在控制器的作用域被破坏时实现订阅/取消订阅侦听器的机制.这是因为我一直在以下列方式使用 rootScope.$on
:
I'm trying to encapsulate the events in a service in order to implement a mechanics to subscribe / unsubscribe the listeners when a controller's scope is destroyed. This because I have been using the rootScope.$on
in the following way:
if(!$rootScope.$$listeners['event']) {
$rootScope.$on('event', function(ev, data){
// do some...
});
}
或
$scope.$on('$destroy', function(ev, data){
// unsubscribe the listener
});
所以我只需要这个事件的一个监听器,当控制器不再活动时,我需要删除现有的监听器,因为我之前注册的函数还在被触发.
So I just need one listener of this event, I need to delete the existing listener when the controller is no longer alive, because the function I registered earlier is still being triggered.
所以我需要在我的控制器上实现一个 $destroy
事件监听器,以便在作用域被销毁时销毁监听器,但我不想每次创建事件时都执行该代码.这就是为什么我要创建一个服务来封装事件.
So I need to implement a $destroy
event listener on my controller, to destroy the listener when the scope is destroyed, but I don't want to do that code each time I create an event.
That's why I want to create a service in where I'm going to encapsulate the events.
angular.module('core').factory('event', [
function() {
var service = {};
service.events = {};
service.on = function(scope, eventId, callback) {
scope.$on('$destroy', function(ev, other){
//unsubscribe
});
service.events[eventId] = callback;
// scope = null; I guess ?
};
service.emit = function(eventId, data){
if (service.events[eventId])
service.events[eventId](data);
else
return new Error('The event is not subscribed');
};
return service;
}
]);
这可以使用 $rootScope 而不是我自己的方法来完成,但是封装了 $rootScope 的 $on
和 $emit
,但最后我会在这里遇到同样的问题.
This could be done using $rootScope instead of my own methods but encapsulating the $on
and $emit
of $rootScope, but at the end I'll have the same issue here.
所以这些是我的问题:
- 将范围引用值传递给服务是一种好的做法吗?
$$destroyed
是什么意思?当这是真的意味着 angularJS 没有对实例的内部引用?- 我应该在我的服务中做一个 scope = null 来让 GC 删除对象还是 angularJS 处理显式删除?
- 有没有更好的方法来做我想做的事?
- Is a good practice to pass the scope ref value to a service?
- What is the meaning of
$$destroyed
? when this is true means that angularJS has no internal references to the instance? - Should I do a scope = null in my service to let GC delete the object or does angularJS handle an explicit delete?
- Is there a better way to do what I want?
推荐答案
你想要完成的基本上是一个事件总线.
您还很好地描述了当前实现的问题.解决这个问题的另一种方法是用你的总线(或任何其他事件总线)装饰 $rootScope .方法如下:
What you are trying to accomplish is basically an event bus.
You have also described very well what is wrong with the current implementation.
A different way to approach the problem is to decorate the $rootScope with your bus (or any other event bus for that matter). Here is how:
app.config(function ($provide) {
$provide.decorator('$rootScope', ['$delegate', '$$bus', function ($delegate, $$bus) {
Object.defineProperty($delegate.constructor.prototype, '$bus', {
get: function () {
var self = this;
return {
subscribe: function () {
var sub = $$bus.subscribe.apply($$bus, arguments);
self.$on('$destroy',
function () {
console.log("unsubscribe!");
sub.unsubscribe();
});
},
publish: $$bus.publish
};
},
enumerable: false
});
return $delegate;
}]);
});
考虑以下 $$bus 实现(为简单起见保持基本):
Considering the following $$bus implementation (kept basic for simplicity):
app.factory('$$bus', function () {
var api = {};
var events = {};
api.subscribe = function (event) {
if (!events.hasOwnProperty(event.name)) {
events[event.name] = [event];
} else {
events[event.name].push(event);
}
return {
unsubscribe: function () {
api.unsubscribe(event);
}
}
};
api.publish = function (eventName, data) {
if (events.hasOwnProperty(eventName)) {
console.log(eventName);
angular.forEach(events[eventName], function (subscriber) {
subscriber.callback.call(this, data);
});
}
};
api.unsubscribe = function (event) {
if (events.hasOwnProperty(event.name)) {
events[event.name].splice(events[event.name].indexOf(event), 1);
if (events[event.name].length == 0) {
delete events[event.name];
}
}
};
return api;
});
现在您要做的就是订阅或发布事件.取消订阅将自动发生(当 $scope 被销毁时):
Now all you have to do is subscribe or publish events. The unsubscribe will take place automatically (when the $scope is destroyed):
$scope.$bus.subscribe({
name: 'test', callback: function (data) {
console.log(data);
}
});
然后发布一个事件:
$scope.$bus.publish('test', {name: "publishing event!"});
要说明的重要点是事件本身订阅到每个单独的 $scope 而不是 $rootScope.这就是你知道"要释放哪个 $scope 的方式.
An important point to make is that the events themselves are subscribed to each individual $scope and not on the $rootScope. That is how you "know" which $scope to release.
我认为它回答了您的问题.考虑到这一点,您显然可以使这种机制更加复杂(例如在视图路由时释放控制器事件侦听器,仅自动取消订阅某些事件等).祝你好运!
I think it answers your question. With that in mind, you can obviously make this mechanism much sophisticated (such as controller event listener released when a view routed, unsubscribe automatically only to certain events, etc.). Good luck!
** 此解决方案采用 此处使用不同的总线框架(除此之外它是相同的).
** This solution is taken form Here which uses a different bus framework (other then that it is the same).
这篇关于如何在服务中封装单个事件和时间事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!