@@ -104,11 +104,11 @@ export class MatMenu implements AfterContentInit, MatMenuPanel<MatMenuItem>, OnI
104
104
private _yPosition : MenuPositionY = this . _defaultOptions . yPosition ;
105
105
private _previousElevation : string ;
106
106
107
- /** Menu items inside the current menu. */
108
- private _items : MatMenuItem [ ] = [ ] ;
107
+ /** All items inside the menu. Includes items nested inside another menu. */
108
+ @ ContentChildren ( MatMenuItem , { descendants : true } ) _allItems : QueryList < MatMenuItem > ;
109
109
110
- /** Emits whenever the amount of menu items changes . */
111
- private _itemChanges = new Subject < MatMenuItem [ ] > ( ) ;
110
+ /** Only the direct descendant menu items. */
111
+ private _directDescendantItems = new QueryList < MatMenuItem > ( ) ;
112
112
113
113
/** Subscription to tab events on the menu panel */
114
114
private _tabSubscription = Subscription . EMPTY ;
@@ -238,19 +238,21 @@ export class MatMenu implements AfterContentInit, MatMenuPanel<MatMenuItem>, OnI
238
238
}
239
239
240
240
ngAfterContentInit ( ) {
241
- this . _keyManager = new FocusKeyManager < MatMenuItem > ( this . _items ) . withWrap ( ) . withTypeAhead ( ) ;
241
+ this . _updateDirectDescendants ( ) ;
242
+ this . _keyManager = new FocusKeyManager ( this . _directDescendantItems ) . withWrap ( ) . withTypeAhead ( ) ;
242
243
this . _tabSubscription = this . _keyManager . tabOut . subscribe ( ( ) => this . closed . emit ( 'tab' ) ) ;
243
244
}
244
245
245
246
ngOnDestroy ( ) {
247
+ this . _directDescendantItems . destroy ( ) ;
246
248
this . _tabSubscription . unsubscribe ( ) ;
247
249
this . closed . complete ( ) ;
248
250
}
249
251
250
252
/** Stream that emits whenever the hovered menu item changes. */
251
253
_hovered ( ) : Observable < MatMenuItem > {
252
- return this . _itemChanges . pipe (
253
- startWith ( this . _items ) ,
254
+ return this . _directDescendantItems . changes . pipe (
255
+ startWith ( this . _directDescendantItems ) ,
254
256
switchMap ( items => merge ( ...items . map ( item => item . _hovered ) ) )
255
257
) ;
256
258
}
@@ -325,35 +327,6 @@ export class MatMenu implements AfterContentInit, MatMenuPanel<MatMenuItem>, OnI
325
327
}
326
328
}
327
329
328
- /**
329
- * Registers a menu item with the menu.
330
- * @docs -private
331
- */
332
- addItem ( item : MatMenuItem ) {
333
- // We register the items through this method, rather than picking them up through
334
- // `ContentChildren`, because we need the items to be picked up by their closest
335
- // `mat-menu` ancestor. If we used `@ContentChildren(MatMenuItem, {descendants: true})`,
336
- // all descendant items will bleed into the top-level menu in the case where the consumer
337
- // has `mat-menu` instances nested inside each other.
338
- if ( this . _items . indexOf ( item ) === - 1 ) {
339
- this . _items . push ( item ) ;
340
- this . _itemChanges . next ( this . _items ) ;
341
- }
342
- }
343
-
344
- /**
345
- * Removes an item from the menu.
346
- * @docs -private
347
- */
348
- removeItem ( item : MatMenuItem ) {
349
- const index = this . _items . indexOf ( item ) ;
350
-
351
- if ( this . _items . indexOf ( item ) > - 1 ) {
352
- this . _items . splice ( index , 1 ) ;
353
- this . _itemChanges . next ( this . _items ) ;
354
- }
355
- }
356
-
357
330
/**
358
331
* Adds classes to the menu panel based on its position. Can be used by
359
332
* consumers to add specific styling based on the position.
@@ -386,4 +359,19 @@ export class MatMenu implements AfterContentInit, MatMenuPanel<MatMenuItem>, OnI
386
359
this . _animationDone . next ( event ) ;
387
360
this . _isAnimating = false ;
388
361
}
362
+
363
+ /**
364
+ * Sets up a stream that will keep track of any newly-added menu items and will update the list
365
+ * of direct descendants. We collect the descendants this way, because `_allItems` can include
366
+ * items that are part of child menus, and using a custom way of registering items is unreliable
367
+ * when it comes to maintaining the item order.
368
+ */
369
+ private _updateDirectDescendants ( ) {
370
+ this . _allItems . changes
371
+ . pipe ( startWith ( this . _allItems ) )
372
+ . subscribe ( ( items : QueryList < MatMenuItem > ) => {
373
+ this . _directDescendantItems . reset ( items . filter ( item => item . _parentMenu === this ) ) ;
374
+ this . _directDescendantItems . notifyOnChanges ( ) ;
375
+ } ) ;
376
+ }
389
377
}
0 commit comments