Skip to content

Commit fb5cdb2

Browse files
crisbetommalerba
authored andcommitted
fix(menu): lazy-rendered content being duplicated when toggling quickly (#11348)
Fixes the lazily-rendered content of a menu being duplicated, if it user toggles the panel too quickly. The issue comes from the fact that we wait for the animation to finish before we detach the content, however we allow the panel to be reopened as soon the closing sequence is kicked off. Fixes #11331.
1 parent 31fd6a2 commit fb5cdb2

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

src/lib/menu/menu-directive.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {throwMatMenuInvalidPositionX, throwMatMenuInvalidPositionY} from './menu
3737
import {MatMenuItem} from './menu-item';
3838
import {MAT_MENU_PANEL, MatMenuPanel} from './menu-panel';
3939
import {MenuPositionX, MenuPositionY} from './menu-positions';
40+
import {AnimationEvent} from '@angular/animations';
4041

4142

4243
/** Default `mat-menu` options that can be overridden. */

src/lib/menu/menu-trigger.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
235235

236236
const menu = this.menu;
237237

238-
this._resetMenu();
239238
this._closeSubscription.unsubscribe();
240239
this._overlayRef.detach();
241240

@@ -245,11 +244,20 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
245244
if (menu.lazyContent) {
246245
// Wait for the exit animation to finish before detaching the content.
247246
menu._animationDone
248-
.pipe(take(1))
249-
.subscribe(() => menu.lazyContent!.detach());
247+
.pipe(filter(event => event.toState === 'void'), take(1))
248+
.subscribe(() => {
249+
menu.lazyContent!.detach();
250+
this._resetMenu();
251+
});
252+
} else {
253+
this._resetMenu();
254+
}
255+
} else {
256+
this._resetMenu();
257+
258+
if (menu.lazyContent) {
259+
menu.lazyContent.detach();
250260
}
251-
} else if (menu.lazyContent) {
252-
menu.lazyContent.detach();
253261
}
254262
}
255263

src/lib/menu/menu.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,31 @@ describe('MatMenu', () => {
418418
expect(fixture.componentInstance.items.length).toBe(0);
419419
}));
420420

421+
it('should wait for the close animation to finish before considering the panel as closed',
422+
fakeAsync(() => {
423+
const fixture = createComponent(SimpleLazyMenu);
424+
fixture.detectChanges();
425+
const trigger = fixture.componentInstance.trigger;
426+
427+
expect(trigger.menuOpen).toBe(false, 'Expected menu to start off closed');
428+
429+
trigger.openMenu();
430+
fixture.detectChanges();
431+
tick(500);
432+
433+
expect(trigger.menuOpen).toBe(true, 'Expected menu to be open');
434+
435+
trigger.closeMenu();
436+
fixture.detectChanges();
437+
438+
expect(trigger.menuOpen)
439+
.toBe(true, 'Expected menu to be considered open while the close animation is running');
440+
tick(500);
441+
fixture.detectChanges();
442+
443+
expect(trigger.menuOpen).toBe(false, 'Expected menu to be closed');
444+
}));
445+
421446
it('should focus the first menu item when opening a lazy menu via keyboard', fakeAsync(() => {
422447
let zone: MockNgZone;
423448
let fixture = createComponent(SimpleLazyMenu, [{

0 commit comments

Comments
 (0)