Skip to content

Commit 781e91e

Browse files
committedJan 10, 2025·
fix(material/menu): lazy content not detached after animation (#30301)
Fixes a regression after #30148 where we no longer detached the lazy content panel, causing its items to be retained until the next open when they get destroyed and re-created. (cherry picked from commit 86a96c9)
1 parent 0aa36f4 commit 781e91e

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed
 

‎src/material/menu/menu-trigger.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,14 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
368368
// Note that we don't wait for the animation to finish if another trigger took
369369
// over the menu, because the panel will end up empty which looks glitchy.
370370
if (menu instanceof MatMenu && this._ownsMenu(menu)) {
371-
this._pendingRemoval = menu._animationDone.pipe(take(1)).subscribe(() => overlayRef.detach());
371+
this._pendingRemoval = menu._animationDone.pipe(take(1)).subscribe(() => {
372+
overlayRef.detach();
373+
menu.lazyContent?.detach();
374+
});
372375
menu._setIsOpen(false);
373376
} else {
374377
overlayRef.detach();
378+
menu?.lazyContent?.detach();
375379
}
376380

377381
if (menu && this._ownsMenu(menu)) {

‎src/material/menu/menu.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import {ScrollDispatcher, ViewportRuler} from '@angular/cdk/scrolling';
1515
import {
1616
ChangeDetectionStrategy,
1717
Component,
18+
Directive,
1819
ElementRef,
1920
EventEmitter,
2021
Input,
22+
OnDestroy,
2123
Output,
2224
Provider,
2325
QueryList,
@@ -1219,6 +1221,40 @@ describe('MatMenu', () => {
12191221
.toBe(true);
12201222
}));
12211223

1224+
it('should detach the lazy content when the menu is closed', fakeAsync(() => {
1225+
let destroyCount = 0;
1226+
1227+
// Note: for some reason doing `spyOn(item, 'ngOnDestroy')` doesn't work, even though a
1228+
// `console.log` shows that the `ngOnDestroy` gets called. We work around it with a custom
1229+
// directive that increments a counter.
1230+
@Directive({selector: '[mat-menu-item]', standalone: false})
1231+
class DestroyChecker implements OnDestroy {
1232+
ngOnDestroy(): void {
1233+
destroyCount++;
1234+
}
1235+
}
1236+
1237+
const fixture = createComponent(SimpleLazyMenu, undefined, [DestroyChecker]);
1238+
fixture.detectChanges();
1239+
fixture.componentInstance.trigger.openMenu();
1240+
fixture.detectChanges();
1241+
tick(500);
1242+
fixture.detectChanges();
1243+
1244+
expect(fixture.componentInstance.items.length).toBe(2);
1245+
expect(destroyCount).toBe(0);
1246+
1247+
fixture.componentInstance.trigger.closeMenu();
1248+
fixture.detectChanges();
1249+
tick(500);
1250+
fixture.detectChanges();
1251+
1252+
expect(fixture.componentInstance.items.length)
1253+
.withContext('Expected items to be removed from query list')
1254+
.toBe(0);
1255+
expect(destroyCount).withContext('Expected ngOnDestroy to have been called').toBe(2);
1256+
}));
1257+
12221258
it('should focus the first menu item when opening a lazy menu via keyboard', async () => {
12231259
const fixture = createComponent(SimpleLazyMenu);
12241260
fixture.autoDetectChanges();

0 commit comments

Comments
 (0)