Skip to content

Cdk listbox control accessor #20071

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

Merged
merged 26 commits into from
Jul 29, 2020

Conversation

nielsr98
Copy link
Contributor

Implemented ControlValueAccessor in listbox, which entailed adding a value property and implementing several required methods. This is done to allow for listbox to be used with form control.

@nielsr98 nielsr98 requested a review from jelbourn as a code owner July 22, 2020 14:58
@nielsr98 nielsr98 requested a review from scriby July 22, 2020 14:58
@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Jul 22, 2020
@nielsr98 nielsr98 added the target: minor This PR is targeted for the next minor release label Jul 22, 2020
@@ -67,6 +68,9 @@ export class CdkOption implements ListKeyManagerOption, Highlightable {
this._disabled = coerceBooleanProperty(value);
}

/** The form value of the option. */
@Input() value: any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the option should be generic so people can type the value if they want to? E.g.

class CdkOption<T = any> {
  value: T;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this seems like a good idea. Do you know if there's a reason why MatOption doesn't do this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, MatOption didn't do this because we didn't know any better back when we wrote it. I would, though, use unknown instead of any for the default

this.deselect(option);
}

for (const value of values) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this break down if the value isn't an array? Based on the typing it could be a string which will work inside the for...of loop, but will loop through character by character.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I neglected to test that out. I'll test out different methods and find a way to handle it. Thanks.

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to Kristiyan's comments; once those are good you should be able to add the unit tests

@@ -67,6 +68,9 @@ export class CdkOption implements ListKeyManagerOption, Highlightable {
this._disabled = coerceBooleanProperty(value);
}

/** The form value of the option. */
@Input() value: any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, MatOption didn't do this because we didn't know any better back when we wrote it. I would, though, use unknown instead of any for the default

@nielsr98
Copy link
Contributor Author

Addressed feedback and added some unit tests.

_onTouched: () => void = () => {};

/** `View -> model callback called when value changes` */
_onChange: (value: any) => void = () => {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_onChange: (value: any) => void = () => {};
_onChange: (value: T) => void = () => {};

@@ -235,6 +244,8 @@ export class CdkListbox implements AfterContentInit, OnDestroy, OnInit {
this._useActiveDescendant = coerceBooleanProperty(shouldUseActiveDescendant);
}

@Input() compareWith: (o1: any, o2: any) => boolean = (a1, a2) => a1 === a2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Input() compareWith: (o1: any, o2: any) => boolean = (a1, a2) => a1 === a2;
@Input() compareWith: (o1: T, o2: T) => boolean = (a1, a2) => a1 === a2;

* Saves a callback function to be invoked when the select's value
* changes from user input. Required to implement ControlValueAccessor.
*/
registerOnChange(fn: (value: any) => void): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
registerOnChange(fn: (value: any) => void): void {
registerOnChange(fn: (value: T) => void): void {

@@ -178,12 +182,18 @@ export class CdkOption implements ListKeyManagerOption, Highlightable {
'[attr.aria-activedescendant]': '_getAriaActiveDescendant()'
}
})
export class CdkListbox implements AfterContentInit, OnDestroy, OnInit {
export class CdkListbox implements AfterContentInit, OnDestroy, OnInit, ControlValueAccessor {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export class CdkListbox implements AfterContentInit, OnDestroy, OnInit, ControlValueAccessor {
export class CdkListbox<T> implements AfterContentInit, OnDestroy, OnInit, ControlValueAccessor {

And all references to CdkOption throughout should be CdkOption<T>

}

/** Sets the select's value. Required to implement ControlValueAccessor. */
writeValue(values: any | any[]): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
writeValue(values: any | any[]): void {
writeValue(value: T | T[]): void {

}

/** Selects an option that has the corresponding given value. */
private _setSelectionByValue(values: any | any[]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private _setSelectionByValue(values: any | any[]) {
private _setSelectionByValue(values: T| T[]) {

}

const valuesArray = coerceArray(values);
console.log(valuesArray);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra log

@@ -655,3 +738,23 @@ class ListboxActiveDescendant {
this.focusedOption = option;
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add test cases for actually using the listbox with a FormControl, and then testing that the interaction works as expected via that FormControl

@nielsr98
Copy link
Contributor Author

Feedback address. Ready for review and check on the changes.

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there's a few lingering any types in the listbox that can be T

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jelbourn jelbourn added lgtm action: merge The PR is ready for merge by the caretaker labels Jul 29, 2020
@jelbourn jelbourn merged commit 5f5f5aa into angular:master Jul 29, 2020
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Aug 29, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants