我试图将事件封装在服务中,以便在控制器的范围被破坏时实现一个机制来订阅/取消订阅监听器。这是因为我一直在以下列方式使用rootScope。$:
if(!$rootScope.$$listeners['event']) {
$rootScope.$on('event', function(ev, data){
// do some...
});
}
或
$scope.$on('$destroy', function(ev, data){
// unsubscribe the listener
});
所以我只需要一个这个事件的监听器,我需要在控制器不再存活时删除现有的监听器,因为我之前注册的功能仍在被触发。
所以我需要在我的控制器上实现一个$ destroy事件监听器,以便在范围被破坏时销毁监听器,但是每次创建事件时我都不想这样做。 这就是为什么我想在我要封装事件的地方创建服务的原因。
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,但最后我还是这里有同样的问题。
所以这些是我的问题:
答案 0 :(得分:3)
您要完成的工作基本上是事件总线。
您还很好地描述了当前实现的错误。
解决问题的另一种方法是使用您的总线(或任何其他事件总线)来装饰$ rootScope。方法如下:
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;
}]);
});
考虑以下$$总线实现(为简单起见保持基本):
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被销毁时):
$scope.$bus.subscribe({
name: 'test', callback: function (data) {
console.log(data);
}
});
稍后发布活动:
$scope.$bus.publish('test', {name: "publishing event!"});
重要要点是事件本身订阅了每个$ scope而不是$ rootScope。这就是你"知道"哪个$范围要发布。
我认为它回答了你的问题。考虑到这一点,您显然可以使此机制更复杂(例如,在视图路由时释放控制器事件侦听器,仅自动取消订阅某些事件等)。 祝你好运!
**此解决方案采用Here形式,它使用不同的总线框架(除此之外它是相同的)。