Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(scope): do not remove the $listeners properties on $destroy #6908

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/ng/rootScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ function $RootScopeProvider(){
}
// recreate the $$destroyed flag
this.$$destroyed = true;

},

/**
Expand Down Expand Up @@ -921,6 +922,7 @@ function $RootScopeProvider(){
* @returns {function()} Returns a deregistration function for this listener.
*/
$on: function(name, listener) {
if (this.$$destroyed) return noop;
var namedListeners = this.$$listeners[name];
if (!namedListeners) {
this.$$listeners[name] = namedListeners = [];
Expand All @@ -933,10 +935,11 @@ function $RootScopeProvider(){
current.$$listenerCount[name] = 0;
}
current.$$listenerCount[name]++;
} while ((current = current.$parent));
} while ((current = current.$parent) && !current.$$destroyed);

var self = this;
return function() {
if (self.$$destroyed) return;
namedListeners[indexOf(namedListeners, listener)] = null;
decrementListenerCount(self, 1, name);
};
Expand Down Expand Up @@ -982,7 +985,7 @@ function $RootScopeProvider(){
listenerArgs = concat([event], arguments, 1),
i, length;

do {
while (scope && !scope.$$destroyed) {
namedListeners = scope.$$listeners[name] || empty;
event.currentScope = scope;
for (i=0, length=namedListeners.length; i<length; i++) {
Expand All @@ -1005,7 +1008,7 @@ function $RootScopeProvider(){
if (stopPropagation) return event;
//traverse upwards
scope = scope.$parent;
} while (scope);
}

return event;
},
Expand Down Expand Up @@ -1113,7 +1116,7 @@ function $RootScopeProvider(){
if (current.$$listenerCount[name] === 0) {
delete current.$$listenerCount[name];
}
} while ((current = current.$parent));
} while ((current = current.$parent) && !current.$$destroyed);
}

/**
Expand Down
58 changes: 58 additions & 0 deletions test/ng/rootScopeSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,64 @@ describe('Scope', function() {
$rootScope.$broadcast(EVENT);
expect(spy.callCount).toBe(1);
}));

it('should not throw when trying to listen to an event on a child scope of an already destroyed scope', inject(function($rootScope) {
var parent = $rootScope.$new(),
child = parent.$new();

parent.$destroy();
expect(function() {
var fn = child.$on('someEvent', angular.noop);
fn();
}).not.toThrow();
}));

it('should not throw when deregistering a listener on a child scope of an already destroyed scope', inject(function($rootScope) {
var parent = $rootScope.$new(),
child = parent.$new(),
fn = child.$on('someEvent', angular.noop);

parent.$destroy();
expect(function() { fn(); }).not.toThrow();
}));

it('should not throw when trying to destroy a child insolated scope of an already destroyed scope', inject(function($rootScope) {
var parent = $rootScope.$new(),
child = parent.$new(true),
fn = child.$on('someEvent', angular.noop);

parent.$destroy();
expect(function() { child.$destroy(); }).not.toThrow();
}));

it('should not throw when trying to listen to an event on an insolated child scope of an already destroyed scope', inject(function($rootScope) {
var parent = $rootScope.$new(),
child = parent.$new(true);

parent.$destroy();
expect(function() {
var fn = child.$on('someEvent', angular.noop);
fn();
}).not.toThrow();
}));

it('should not throw when deregistering a listener on an insolated child scope of an already destroyed scope', inject(function($rootScope) {
var parent = $rootScope.$new(),
child = parent.$new(true),
fn = child.$on('someEvent', angular.noop);

parent.$destroy();
expect(function() { fn(); }).not.toThrow();
}));

it('should not throw when trying to emit from an insolated child scope of a destroyed scope', inject(function($rootScope) {
var parent = $rootScope.$new(),
child = parent.$new(true),
fn = child.$on('someEvent', angular.noop);

parent.$destroy();
expect(function() { child.$emit('someEvent'); }).not.toThrow();
}));
});


Expand Down