mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(experimental/dialog): emitting events twice on some browsers (#13587)
Fixes the `Dialog` emitting some of its events twice on certain browsers due to an issue in `@angular/animations` which invokes the animation `done` callback twice. Fixes #13585.
This commit is contained in:
committed by
Andrew Seguin
parent
3ee9b47a3c
commit
ba7d53d5bb
@@ -30,6 +30,7 @@ import {
|
||||
ViewEncapsulation,
|
||||
} from '@angular/core';
|
||||
import {Subject} from 'rxjs';
|
||||
import {distinctUntilChanged} from 'rxjs/operators';
|
||||
import {DialogConfig} from './dialog-config';
|
||||
|
||||
|
||||
@@ -61,7 +62,7 @@ export function throwDialogContentAlreadyAttachedError() {
|
||||
host: {
|
||||
'[@dialog]': '_state',
|
||||
'(@dialog.start)': '_onAnimationStart($event)',
|
||||
'(@dialog.done)': '_onAnimationDone($event)',
|
||||
'(@dialog.done)': '_animationDone.next($event)',
|
||||
},
|
||||
})
|
||||
export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy {
|
||||
@@ -103,6 +104,9 @@ export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy {
|
||||
/** A subject emitting after the dialog exits the view. */
|
||||
_afterExit: Subject<void> = new Subject();
|
||||
|
||||
/** Stream of animation `done` events. */
|
||||
_animationDone = new Subject<AnimationEvent>();
|
||||
|
||||
constructor(
|
||||
private _elementRef: ElementRef<HTMLElement>,
|
||||
private _focusTrapFactory: FocusTrapFactory,
|
||||
@@ -111,11 +115,30 @@ export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy {
|
||||
/** The dialog configuration. */
|
||||
public _config: DialogConfig) {
|
||||
super();
|
||||
|
||||
// We use a Subject with a distinctUntilChanged, rather than a callback attached to .done,
|
||||
// because some browsers fire the done event twice and we don't want to emit duplicate events.
|
||||
// See: https://github.com/angular/angular/issues/24084
|
||||
this._animationDone.pipe(distinctUntilChanged((x, y) => {
|
||||
return x.fromState === y.fromState && x.toState === y.toState;
|
||||
})).subscribe(event => {
|
||||
// Emit lifecycle events based on animation `done` callback.
|
||||
if (event.toState === 'enter') {
|
||||
this._autoFocusFirstTabbableElement();
|
||||
this._afterEnter.next();
|
||||
}
|
||||
|
||||
if (event.fromState === 'enter' && (event.toState === 'void' || event.toState === 'exit')) {
|
||||
this._returnFocusAfterDialog();
|
||||
this._afterExit.next();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Destroy focus trap to place focus back to the element focused before the dialog opened. */
|
||||
ngOnDestroy() {
|
||||
this._focusTrap.destroy();
|
||||
this._animationDone.complete();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,19 +177,6 @@ export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/** Emit lifecycle events based on animation `done` callback. */
|
||||
_onAnimationDone(event: AnimationEvent) {
|
||||
if (event.toState === 'enter') {
|
||||
this._autoFocusFirstTabbableElement();
|
||||
this._afterEnter.next();
|
||||
}
|
||||
|
||||
if (event.fromState === 'enter' && (event.toState === 'void' || event.toState === 'exit')) {
|
||||
this._returnFocusAfterDialog();
|
||||
this._afterExit.next();
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts the dialog exit animation. */
|
||||
_startExiting(): void {
|
||||
this._state = 'exit';
|
||||
|
||||
Reference in New Issue
Block a user