Skip to content

Commit f9bbbe7

Browse files
Eddmanjelbourn
authored andcommitted
fix(unique-selection-dispatcher): remove listeners on destroy (#5164)
1 parent 2239668 commit f9bbbe7

File tree

5 files changed

+79
-19
lines changed

5 files changed

+79
-19
lines changed

src/lib/button-toggle/button-toggle.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
HostBinding,
1717
Input,
1818
OnInit,
19+
OnDestroy,
1920
Optional,
2021
Output,
2122
QueryList,
@@ -270,7 +271,7 @@ export class MdButtonToggleGroupMultiple extends _MdButtonToggleGroupMixinBase
270271
'class': 'mat-button-toggle'
271272
}
272273
})
273-
export class MdButtonToggle implements OnInit {
274+
export class MdButtonToggle implements OnInit, OnDestroy {
274275
/** Whether or not this button toggle is checked. */
275276
private _checked: boolean = false;
276277

@@ -286,6 +287,9 @@ export class MdButtonToggle implements OnInit {
286287
/** Whether or not the button toggle is a single selection. */
287288
private _isSingleSelector: boolean = false;
288289

290+
/** Unregister function for _buttonToggleDispatcher **/
291+
private _removeUniqueSelectionListener: () => void = () => {};
292+
289293
@ViewChild('input') _inputElement: ElementRef;
290294

291295
/** The parent button toggle group (exclusive selection). Optional. */
@@ -371,11 +375,12 @@ export class MdButtonToggle implements OnInit {
371375
this.buttonToggleGroupMultiple = toggleGroupMultiple;
372376

373377
if (this.buttonToggleGroup) {
374-
_buttonToggleDispatcher.listen((id: string, name: string) => {
375-
if (id != this.id && name == this.name) {
376-
this.checked = false;
377-
}
378-
});
378+
this._removeUniqueSelectionListener =
379+
_buttonToggleDispatcher.listen((id: string, name: string) => {
380+
if (id != this.id && name == this.name) {
381+
this.checked = false;
382+
}
383+
});
379384

380385
this._type = 'radio';
381386
this.name = this.buttonToggleGroup.name;
@@ -445,4 +450,9 @@ export class MdButtonToggle implements OnInit {
445450
event.value = this._value;
446451
this.change.emit(event);
447452
}
453+
454+
// Unregister buttonToggleDispatcherListener on destroy
455+
ngOnDestroy(): void {
456+
this._removeUniqueSelectionListener();
457+
}
448458
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {UniqueSelectionDispatcher} from './unique-selection-dispatcher';
2+
3+
4+
describe('Unique selection dispatcher', () => {
5+
6+
describe('register', () => {
7+
it('once unregistered the listener must not be called on notify', (done) => {
8+
let dispatcher: UniqueSelectionDispatcher = new UniqueSelectionDispatcher();
9+
let called = false;
10+
11+
// Register first listener
12+
dispatcher.listen(() => {
13+
called = true;
14+
});
15+
16+
// Register a listener
17+
let deregisterFn = dispatcher.listen(() => {
18+
done.fail('Should not be called');
19+
});
20+
21+
// Unregister
22+
deregisterFn();
23+
24+
// Call registered listeners
25+
dispatcher.notify('testId', 'testName');
26+
27+
expect(called).toBeTruthy('Registered listener must be called.');
28+
29+
done();
30+
});
31+
});
32+
});

src/lib/core/coordination/unique-selection-dispatcher.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,17 @@ export class UniqueSelectionDispatcher {
3636
}
3737
}
3838

39-
/** Listen for future changes to item selection. */
40-
listen(listener: UniqueSelectionDispatcherListener) {
39+
/**
40+
* Listen for future changes to item selection.
41+
* @return Function used to unregister listener
42+
**/
43+
listen(listener: UniqueSelectionDispatcherListener): () => void {
4144
this._listeners.push(listener);
45+
return () => {
46+
this._listeners = this._listeners.filter((registered: UniqueSelectionDispatcherListener) => {
47+
return listener !== registered;
48+
});
49+
};
4250
}
4351
}
4452

src/lib/expansion/accordion-item.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,24 @@ export class AccordionItem implements OnDestroy {
4848
}
4949
private _expanded: boolean;
5050

51+
/** Unregister function for _expansionDispatcher **/
52+
private _removeUniqueSelectionListener: () => void = () => {};
53+
5154
constructor(@Optional() public accordion: CdkAccordion,
5255
protected _expansionDispatcher: UniqueSelectionDispatcher) {
53-
_expansionDispatcher.listen((id: string, accordionId: string) => {
54-
if (this.accordion && !this.accordion.multi &&
55-
this.accordion.id === accordionId && this.id !== id) {
56-
this.expanded = false;
57-
}
58-
});
56+
this._removeUniqueSelectionListener =
57+
_expansionDispatcher.listen((id: string, accordionId: string) => {
58+
if (this.accordion && !this.accordion.multi &&
59+
this.accordion.id === accordionId && this.id !== id) {
60+
this.expanded = false;
61+
}
62+
});
5963
}
6064

6165
/** Emits an event for the accordion item being destroyed. */
6266
ngOnDestroy() {
6367
this.destroyed.emit();
68+
this._removeUniqueSelectionListener();
6469
}
6570

6671
/** Toggles the expanded state of the accordion item. */

src/lib/radio/radio.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,9 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
457457
/** Reference to the current focus ripple. */
458458
private _focusRipple: RippleRef | null;
459459

460+
/** Unregister function for _radioDispatcher **/
461+
private _removeUniqueSelectionListener: () => void = () => {};
462+
460463
/** The native `<input type=radio>` element */
461464
@ViewChild('input') _inputElement: ElementRef;
462465

@@ -472,11 +475,12 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
472475
// TODO(jelbourn): Assert that there's no name binding AND a parent radio group.
473476
this.radioGroup = radioGroup;
474477

475-
_radioDispatcher.listen((id: string, name: string) => {
476-
if (id != this.id && name == this.name) {
477-
this.checked = false;
478-
}
479-
});
478+
this._removeUniqueSelectionListener =
479+
_radioDispatcher.listen((id: string, name: string) => {
480+
if (id != this.id && name == this.name) {
481+
this.checked = false;
482+
}
483+
});
480484
}
481485

482486
/** Focuses the radio button. */
@@ -512,6 +516,7 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
512516

513517
ngOnDestroy() {
514518
this._focusOriginMonitor.stopMonitoring(this._inputElement.nativeElement);
519+
this._removeUniqueSelectionListener();
515520
}
516521

517522
/** Dispatch change event with current value. */

0 commit comments

Comments
 (0)