Skip to content

Commit 416d309

Browse files
committed
fix(select): inconsistent openedChange event dispatched between browsers
Fixes the `openedChange` event being dispatched once when a select is closed on most browsers, but twice in IE and Edge. Fixes #11444.
1 parent b9041e3 commit 416d309

File tree

2 files changed

+30
-18
lines changed

2 files changed

+30
-18
lines changed

src/lib/select/select.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
class="mat-select-panel {{ _getPanelTheme() }}"
3636
[ngClass]="panelClass"
3737
[@transformPanel]="multiple ? 'showing-multiple' : 'showing'"
38-
(@transformPanel.done)="_onPanelDone()"
38+
(@transformPanel.done)="_panelDoneAnimatingStream.next($event.toState)"
3939
[style.transformOrigin]="_transformOrigin"
4040
[class.mat-select-panel-done-animating]="_panelDoneAnimating"
4141
[style.font-size.px]="_triggerFontSize"

src/lib/select/select.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,15 @@ import {
7575
} from '@angular/material/core';
7676
import {MatFormField, MatFormFieldControl} from '@angular/material/form-field';
7777
import {defer, merge, Observable, Subject} from 'rxjs';
78-
import {filter, map, startWith, switchMap, take, takeUntil} from 'rxjs/operators';
78+
import {
79+
filter,
80+
map,
81+
startWith,
82+
switchMap,
83+
take,
84+
takeUntil,
85+
distinctUntilChanged,
86+
} from 'rxjs/operators';
7987
import {matSelectAnimations} from './select-animations';
8088
import {
8189
getMatSelectDynamicMultipleError,
@@ -264,6 +272,9 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
264272
/** Whether the panel's animation is done. */
265273
_panelDoneAnimating: boolean = false;
266274

275+
/** Emits when the panel element is finished transforming in. */
276+
_panelDoneAnimatingStream = new Subject<string>();
277+
267278
/** Strategy that will be used to handle scrolling while the select panel is open. */
268279
_scrollStrategy = this._scrollStrategyFactory();
269280

@@ -470,6 +481,23 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
470481
ngOnInit() {
471482
this._selectionModel = new SelectionModel<MatOption>(this.multiple, undefined, false);
472483
this.stateChanges.next();
484+
485+
// We need `distinctUntilChanged` here, because some browsers will
486+
// fire the animation end event twice for the same animation. See:
487+
// https://github.com/angular/angular/issues/24084
488+
this._panelDoneAnimatingStream
489+
.pipe(distinctUntilChanged(), takeUntil(this._destroy))
490+
.subscribe(() => {
491+
if (this.panelOpen) {
492+
this._scrollTop = 0;
493+
this.openedChange.emit(true);
494+
} else {
495+
this.openedChange.emit(false);
496+
this._panelDoneAnimating = false;
497+
this.overlayDir.offsetX = 0;
498+
this._changeDetectorRef.markForCheck();
499+
}
500+
});
473501
}
474502

475503
ngAfterContentInit() {
@@ -674,22 +702,6 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
674702
}
675703
}
676704

677-
/**
678-
* When the panel element is finished transforming in (though not fading in), it
679-
* emits an event and focuses an option if the panel is open.
680-
*/
681-
_onPanelDone(): void {
682-
if (this.panelOpen) {
683-
this._scrollTop = 0;
684-
this.openedChange.emit(true);
685-
} else {
686-
this.openedChange.emit(false);
687-
this._panelDoneAnimating = false;
688-
this.overlayDir.offsetX = 0;
689-
this._changeDetectorRef.markForCheck();
690-
}
691-
}
692-
693705
/**
694706
* When the panel content is done fading in, the _panelDoneAnimating property is
695707
* set so the proper class can be added to the panel.

0 commit comments

Comments
 (0)