fix(collections): clean up UniqueSelectionDispatcher listeners on destroy (#9673)

* Clears out all of the registered listeners in the `UniqueSelectionDispatcher` when the module is unloaded to avoid potential memory leaks due to references inside the listeners.
* Cleans up the unit test setup for the dispatcher to align it with the rest of the project.
This commit is contained in:
Kristiyan Kostadinov
2018-02-01 20:26:44 +01:00
committed by tinayuangao
parent efb03c9ec8
commit 26b0635d1f
2 changed files with 30 additions and 21 deletions
@@ -2,31 +2,36 @@ import {UniqueSelectionDispatcher} from './unique-selection-dispatcher';
describe('Unique selection dispatcher', () => {
let dispatcher: UniqueSelectionDispatcher;
describe('register', () => {
it('once unregistered the listener must not be called on notify', (done) => {
let dispatcher: UniqueSelectionDispatcher = new UniqueSelectionDispatcher();
let called = false;
beforeEach(() => dispatcher = new UniqueSelectionDispatcher());
// Register first listener
dispatcher.listen(() => {
called = true;
});
it('should notify registered listeners', () => {
const spy = jasmine.createSpy('listen handler');
// Register a listener
let deregisterFn = dispatcher.listen(() => {
done.fail('Should not be called');
});
dispatcher.listen(spy);
dispatcher.notify('id', 'name');
// Unregister
deregisterFn();
expect(spy).toHaveBeenCalledWith('id', 'name');
});
// Call registered listeners
dispatcher.notify('testId', 'testName');
it('should not notify unregistered listeners', () => {
const spy = jasmine.createSpy('listen handler');
const unregister = dispatcher.listen(spy);
expect(called).toBeTruthy('Registered listener must be called.');
unregister();
dispatcher.notify('id', 'name');
done();
});
expect(spy).not.toHaveBeenCalled();
});
it('should remove all listeners when destroyed', () => {
const spy = jasmine.createSpy('listen handler');
dispatcher.listen(spy);
dispatcher.ngOnDestroy();
dispatcher.notify('id', 'name');
expect(spy).not.toHaveBeenCalled();
});
});
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable, Optional, SkipSelf} from '@angular/core';
import {Injectable, Optional, SkipSelf, OnDestroy} from '@angular/core';
// Users of the Dispatcher never need to see this type, but TypeScript requires it to be exported.
@@ -22,7 +22,7 @@ export type UniqueSelectionDispatcherListener = (id: string, name: string) => vo
* less error-prone if they are simply passed through when the events occur.
*/
@Injectable()
export class UniqueSelectionDispatcher {
export class UniqueSelectionDispatcher implements OnDestroy {
private _listeners: UniqueSelectionDispatcherListener[] = [];
/**
@@ -48,6 +48,10 @@ export class UniqueSelectionDispatcher {
});
};
}
ngOnDestroy() {
this._listeners = [];
}
}
/** @docs-private */