-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
fix(selection-list): fix option value coercion and selection events #6901
Conversation
src/lib/list/selection-list.ts
Outdated
@@ -97,7 +97,7 @@ export class MdListOption extends _MdListOptionMixinBase | |||
|
|||
@Input() | |||
get value() { return this._value; } | |||
set value( val: any) { this._value = coerceBooleanProperty(val); } | |||
set value( val: any) { this._value = val; } |
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.
The getter and setter can be removed if you're no longer coercing
I have some statements about this component. 1 - Why is there an event for 2 - Rename the interface(class - see below) from 3 - Shouldn't Actual:
Proposal (similar to checkbox):
4 - Is the Actual:
-- 5 - Shouldn't Actual: Proposal: |
Yeah, that's a good point. It should just have one |
In my current project I am watching the destroyed event so I can remove options that are no longer available. You could probably use the changes QueryList in selectionList, but you'd have to figure out which ones were removed. Looking at the Material code again, the MdSelectionList itself subscribes to the destroyed events to figure out which option to focus to once one is destroyed. I haven't used enough elements to see one destroyed yet, though. |
@jelbourn what do you think about other statements? |
@rafaelss95 those should be covered under #6677 |
I went ahead and pushed changes that were relevant to the original issue. I didn't rename MdSelectionListOptionEvent, but I changed it to a class now so it stayed consistent with similar classes. I skipped the focus issue, but it should probably be a Subject instead of an EventEmitter looking at other classes like MdChip where it is only used internally.
|
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.
LGTM
Hello, Everybody! |
please rebase |
rebase with master or _presubmit? Looks like someone else made some of the the same changes. (probably a dumb question but I don't do this often. I'm working on rebasing with master right now.) Edit: Rebased it with master. Everything looked ok |
Scratch that, I broke it. Resolved the conflict the wrong way and erased the changes made to selection-list! I see master has moved some more so I'll rebase again and not destroy the changes this time. 😄 |
@dereekb Would you mind rebasing one more time? |
Sure. I'm rebasing right now, but it looks like a lot of the changes in this pull request are already on master one way or another from other pull requests. The only changes are changing MatSelectionListOptionEvent to a class, and merging selectChange and deselected together into a single event emitter, that last one being a breaking change. |
Noticed I need to rebase it again. Working on that now. |
…event emitters * md-list-option value is no longer coerced to a boolean value * md-list-option EventEmitters selectedChange and deselected now emit an event when an option is selected/deselected Fixes #6864
Ok, rebased and all the unit tests passed on my end. I want to bring up a slight change from this rebase: I removed _selected and instead have the getter use the selectionList's selectedOptions isSelected() for whether or not the object is selected. My reasoning is that the selection's state shouldn't be in two places (by this I mean MatSelectionList and other components check selectedOptions for is selected, while the MatListOption checks itself), and the performance change is minimal since a Set is used for selectedOptions. Another thing, since selectedOptions in MatSelectionList is publicly accessible, externally modifying selectedOptions should also send selectionChange events on the MatListOption right? I don't think it would, so it's something to consider. |
Can you guys already pull it? I really need this fix in my project. |
src/lib/list/selection-list.spec.ts
Outdated
@@ -579,3 +658,28 @@ class SelectionListWithTabindexBinding { | |||
tabIndex: number; | |||
disabled: boolean; | |||
} | |||
|
|||
@Component({template: ` | |||
<mat-selection-list id = "selection-list-5"> |
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.
remove the spaces around: id="selection-list-5"
?
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.
I don't know if I hit the format button on my side, but there are spaces around most of the unit test component's id's in this file.
I'll fix them.
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.
You're right. There are a lot of strange spaces in this spec file.
src/lib/list/selection-list.ts
Outdated
@@ -174,6 +169,16 @@ export class MatListOption extends _MatListOptionMixinBase | |||
this.selectionList._setFocusedOption(this); | |||
} | |||
|
|||
/** Creates a selection event object from the specified option. */ | |||
private _createChangeEvent(option: MatListOption = this): MatSelectionListOptionEvent { | |||
let event = new MatSelectionListOptionEvent(); |
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.
const
?
src/lib/list/selection-list.ts
Outdated
option: MatListOption; | ||
selected: boolean; | ||
} |
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.
I'd keep the consistency between this and other components:
/** Change event object emitted by MatListOption */
export class MatListOptionChange {
/** The source MatListOption of the event. */
source: MatListOption;
/** The new `selected` value of the option. */
selected: boolean;
}
See checkbox for example.
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.
So checked instead of selected? Gotcha.
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.
In fact, all changes above (to keep consistency). Also with comments so it can be shown in docs.
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.
By others you mean other checkboxes? Add comments?
Changing it from selected to checked seems like a step in the wrong direction, since it is MatListOption.selected, not MatListOption.checked.
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.
You're right (bad copy). Comments === docs description.
What about source
instead of option
? All change
events use source
?
Also, MatListOptionChange
instead of MatSelectionListOptionEvent
?
I updated the code above.
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.
Sounds good, I'll change that.
src/lib/list/selection-list.ts
Outdated
/** Whether the option is selected. */ | ||
@Input() | ||
get selected() { return this._selected; } | ||
get selected() { return this.selectionList.selectedOptions.isSelected(this); } |
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.
Can you add :boolean
here? get selected(): boolean
.
src/lib/list/selection-list.ts
Outdated
@@ -97,34 +97,29 @@ export class MatListOption extends _MatListOptionMixinBase | |||
/** Whether the label should appear before or after the checkbox. Defaults to 'after' */ | |||
@Input() checkboxPosition: 'before' | 'after' = 'after'; | |||
|
|||
/** Value of the option */ | |||
@Input() value: any; | |||
|
|||
/** Whether the option is disabled. */ | |||
@Input() | |||
get disabled() { return (this.selectionList && this.selectionList.disabled) || this._disabled; } |
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.
: boolean
?
src/lib/list/selection-list.ts
Outdated
@@ -97,34 +97,29 @@ export class MatListOption extends _MatListOptionMixinBase | |||
/** Whether the label should appear before or after the checkbox. Defaults to 'after' */ | |||
@Input() checkboxPosition: 'before' | 'after' = 'after'; | |||
|
|||
/** Value of the option */ | |||
@Input() value: any; | |||
|
|||
/** Whether the option is disabled. */ | |||
@Input() | |||
get disabled() { return (this.selectionList && this.selectionList.disabled) || this._disabled; } | |||
set disabled(value: any) { this._disabled = coerceBooleanProperty(value); } |
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.
: boolean
?
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.
Value is the actual selection option value.
We don't want to coerce it to a boolean, again! 😛
Edit: Misread
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.
Well... for selected
we have: set selected(value: boolean) { const isSelected = coerceBooleanProperty(value);
...
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.
Gotcha, fixed.
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.
Ah, ok. The highlighted code I see is both on this section:
/** Value of the option */
- @Input() value: any;
So I was kind of guessing what you meant. Making those changes now.
src/lib/list/selection-list.ts
Outdated
@@ -111,7 +111,7 @@ export class MatListOption extends _MatListOptionMixinBase | |||
@Input() | |||
get selected(): boolean { return this.selectionList.selectedOptions.isSelected(this); } | |||
set selected(value: boolean) { | |||
const isSelected = coerceBooleanProperty(value); | |||
const isSelected: boolean = coerceBooleanProperty(value); |
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.
I think you misunderstood what I mean... I mean that we have the following signature for selected: set selected(value: boolean) { ... = coerceBooleanProperty(value); }
, so the disabled
should follow the same thing.
Basically change the signature of disabled from set disabled(value: any) {
to set disabled(value: boolean) {
.
Did you get it now?
This :boolean added here is unnecessary.
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.
Yea, the highlighted section GitHub was showing was:
/** Value of the option */
- @Input() value: any;
So I didn't really get what you were talking about. It's changed now and I removed the unnecessary :boolean.
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.
Aha, ok. Nice :)
Will you remove this :boolean
in another commit?
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.
That's the second time today I've committed without first hitting save. Done.
src/lib/list/selection-list.ts
Outdated
@@ -51,16 +51,18 @@ export const _MatSelectionListMixinBase = | |||
export class MatListOptionBase {} | |||
export const _MatListOptionMixinBase = mixinDisableRipple(MatListOptionBase); | |||
|
|||
/** Event emitted by a selection-list whenever the state of an option is changed. */ | |||
/** Change event object emitted by MatListOption */ | |||
export class MatSelectionListOptionEvent { |
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.
MatListOptionChange
?
Can you check on the tests and get them green? |
The tests that failed aren't related to the changes (they have to do with MatAutocomplete).
Those failed tests are related to (and will be fixed by) #7743, I think. |
FYI - material docs specify the event |
Yea, the docs (probably) need to be updated to reflect these changes. selected/deselected were removed and replaced with selectionChange, since having a single selectionChange is more consistent in terms of the rest of Angular Material implementations. As for the selectChange/selectionChange name, there was another discussion thread where I believe selectionChange was picked as the more desirable. I don't see the reference in this thread anymore, but it was about naming consistency and updating across a number of different Material components. |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Fixes #6864