Skip to content

Commit

Permalink
fix(scope): do not remove the listeners properties on destroy
Browse files Browse the repository at this point in the history
Destroying the listener properties cases many issues when any of these
operations is being performed on a destroyed scope or a child scope
* Registering a new event
* Deregistering an existing event
* Destroying an insolated child scope with a destroyed parent
* Sending an `$emit` from a child scope with a destroyed parent

Closes angular#6897
  • Loading branch information
lgalfaso committed Mar 29, 2014
1 parent 908ab52 commit cab6201
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 3 deletions.
6 changes: 3 additions & 3 deletions src/ng/rootScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -745,12 +745,12 @@ function $RootScopeProvider(){
// - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
for (var prop in this) {
if (hasOwnProperty.call(this, prop)) {
if (hasOwnProperty.call(this, prop) && prop !== '$$destroyed' &&
// Avoid removing the listener properties as this causes many issues like #6897
prop !== '$$listeners' && prop !== '$$listenerCount') {
this[prop] = null;
}
}
// recreate the $$destroyed flag
this.$$destroyed = true;
},

/**
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

0 comments on commit cab6201

Please sign in to comment.