-
Notifications
You must be signed in to change notification settings - Fork 6.8k
refactor(cdk-experimental/menu): Break MenuItems into directives #19688
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
eef0518
20a9915
e929ad8
2918e80
96a5e9e
0d7f807
988412d
a5303e8
cc2cfd8
400e55c
80b9bcd
f9825f5
33513a3
96bf59d
042e321
299db25
97db1ae
26f90b6
919dc03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,19 @@ | |
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Directive, Output, EventEmitter} from '@angular/core'; | ||
import {MenuItem} from './menu-item-interface'; | ||
import { | ||
Directive, | ||
Output, | ||
EventEmitter, | ||
ContentChildren, | ||
AfterContentInit, | ||
QueryList, | ||
OnDestroy, | ||
jelbourn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} from '@angular/core'; | ||
import {UniqueSelectionDispatcher} from '@angular/cdk/collections'; | ||
import {takeUntil} from 'rxjs/operators'; | ||
import {CdkMenuItemSelectable} from './menu-item-selectable'; | ||
import {CdkMenuItem} from './menu-item'; | ||
|
||
/** | ||
* Directive which acts as a grouping container for `CdkMenuItem` instances with | ||
|
@@ -19,18 +30,45 @@ import {MenuItem} from './menu-item-interface'; | |
host: { | ||
'role': 'group', | ||
}, | ||
providers: [{provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher}], | ||
}) | ||
export class CdkMenuGroup { | ||
export class CdkMenuGroup implements AfterContentInit, OnDestroy { | ||
/** Emits the element when checkbox or radiobutton state changed */ | ||
@Output() change: EventEmitter<MenuItem> = new EventEmitter(); | ||
@Output() readonly change: EventEmitter<CdkMenuItem> = new EventEmitter(); | ||
|
||
/** List of menuitemcheckbox or menuitemradio elements which reside in this group */ | ||
@ContentChildren(CdkMenuItemSelectable, {descendants: true}) | ||
private readonly _selectableItems: QueryList<CdkMenuItemSelectable>; | ||
|
||
/** Emits when the _selectableItems QueryList triggers a change */ | ||
private readonly _selectableChanges: EventEmitter<void> = new EventEmitter(); | ||
|
||
ngAfterContentInit() { | ||
this._registerMenuSelectionListeners(); | ||
} | ||
|
||
/** | ||
* Emits events for the clicked MenuItem | ||
* @param menuItem The clicked MenuItem to handle | ||
* Register the child selectable elements with the change emitter and ensure any new child | ||
* elements do so as well. | ||
*/ | ||
_registerTriggeredItem(menuItem: MenuItem) { | ||
if (menuItem.role !== 'menuitem') { | ||
this.change.emit(menuItem); | ||
} | ||
private _registerMenuSelectionListeners() { | ||
this._selectableItems.forEach(selectable => this._registerClickListener(selectable)); | ||
jelbourn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
this._selectableItems.changes.subscribe((selectableItems: QueryList<CdkMenuItemSelectable>) => { | ||
this._selectableChanges.next(); | ||
selectableItems.forEach(selectable => this._registerClickListener(selectable)); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you move the logic inside ngAfterContentInit() {
this._registerMenuSelectionListeners();
} This related to my philosophy on naming methods for what they do rather than how/when they're called. This also helps things that you would call from lifecycle hooks more single-responsibility |
||
} | ||
|
||
/** Register each selectable to emit on the change Emitter when clicked */ | ||
private _registerClickListener(selectable: CdkMenuItemSelectable) { | ||
selectable.clicked | ||
.pipe(takeUntil(this._selectableChanges)) | ||
.subscribe(() => this.change.next(selectable)); | ||
} | ||
|
||
ngOnDestroy() { | ||
this._selectableChanges.next(); | ||
this._selectableChanges.complete(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and elsewhere, the test code may be a little clearer if you replace 'e' with a more descriptive name (such as debugElement or menuItemRadioDebugElement). If you're following the CDK convention, however, you should probably continue doing so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This convention is totally acceptable (even encouraged) in this repo. I find that, for simple map/find/filter functions, one-character names make the operation easier to read.