-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
feat(chip-list): Implement FormFieldControl and ControlValueAccessor in chip list #6686
Conversation
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.
Still need to take a closer look at the code, but something I noticed in the demo: it's hard to focus the actual input sometimes. Clicking on the md-form-field
/ md-chip-list
should focus the input rather than one of the chips
src/lib/chips/chip-input.ts
Outdated
} | ||
}) | ||
export class MdChipInput { | ||
export class MdChipInput extends MdInput { |
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.
Should we check for something like: <input mdInput mdChipInputFor="chips">
and throw an error? People might just be used to adding the mdInput
and I'm not sure what would happen in this case if they did that... I assume it would break things.
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 it's okay to use something like <input mdInput mdChipInputFor="chips">
.
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.
They won't conflict with each other since this extends MdInput
? It seems like at the very least you would be doing twice the amount of work for no reason, since both directives would react to every change in value and try to do things.
Actually looking at the code a little more closely, I see that MdChipList
implements MdFormFieldControl
and handles communication with the MdFormField
so why does this need to extend MdInput
at all? If there's enough shared stuff between this and MdInput
you could maybe factor it into a common base class, but I don't think it really makes sense for this to directly extend MdInput
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.
@tinayuangao still curious about why this needs to extend MdInput
, seems unnecessary to me.
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.
@mmalerba Changed MdChipInput
to not extends MdInput
. Added focus()
, focused
and empty
to work with chip list. Please take another look. Thanks!
8ae9963
to
eb74659
Compare
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.
Are there breaking changes that should be marked in this PR?
@@ -19,12 +38,13 @@ export interface MdChipInputEvent { | |||
@Directive({ |
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.
Could you add a class description for this directive that talks about how it operates as a control for md-form-field
?
src/lib/chips/chip-list.ts
Outdated
protected _ariaDescribedby: string; | ||
|
||
/** Id of the chip list */ | ||
protected _id: string; |
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.
There's an extra space after protected
on some of these properties
src/lib/chips/chip-list.ts
Outdated
|
||
/** For FormFieldControl. If any of the chips has focus, or the chip input has focus */ | ||
get focused() { | ||
return !!this.chips.find((chip) => chip._hasFocus) || |
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 can use some
instead of find
and you won't need the !!
src/lib/chips/chip-list.ts
Outdated
return this._chipInput.placeholder; | ||
} else { | ||
return this._placeholder; | ||
} |
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.
Use a ternary?
return this._chipInput ? this._chipInput.placeholder : this._placeholder;
styleUrls: ['chips.css'], | ||
encapsulation: ViewEncapsulation.None, | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class MdChipList implements AfterContentInit, OnDestroy { | ||
export class MdChipList implements MdFormFieldControl<any>, ControlValueAccessor, |
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.
Could you file an issue to track sharing common code for role="listbox"
components in cdk/listbox
? It would be good to capture what in particular would belong there
src/lib/chips/chip-list.ts
Outdated
} else if (chipIndex - 1 >= 0) { | ||
this._keyManager.setActiveItem(chipIndex - 1); | ||
} | ||
protected _updateKeyManager(chip: MdChip) { |
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.
Add function description?
src/lib/chips/chip-list.ts
Outdated
|
||
_setSelectionByValue(value: any, isUserInput: boolean = true) { | ||
this._clearSelection(); | ||
this.chips.forEach((chip) => chip.deselect()); |
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.
Nit: don't need parenthesis around chip
(in any arrow function where the type is implicit)
src/lib/chips/chip-list.ts
Outdated
_setSelectionByValue(value: any, isUserInput: boolean = true) { | ||
this._clearSelection(); | ||
this.chips.forEach((chip) => chip.deselect()); | ||
const isArray = Array.isArray(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 wouldn't add the extra isArray
variable here and would just use isArray()
in the if
block
src/lib/chips/chip-list.ts
Outdated
*/ | ||
private _selectValue(value: any, isUserInput: boolean = true): MdChip | undefined { | ||
|
||
const correspondingChip = this.chips.find((chip: MdChip) => { |
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 MdChip
type should be implicit
eb74659
to
c19c278
Compare
Comments addressed. Added breaking changes to description. For focus, user can set <md-chip-list [tabIndex]="-1" #chipList>
<md-chip>chip one</md-chip>
<input mdChipInputFor="chipList">
</md-chip-list> And user can use left and right arrow keys to navigate to chips when |
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.
Typos and a few questions
src/lib/chips/chip-input.ts
Outdated
|
||
export interface MdChipInputEvent { | ||
input: HTMLInputElement; | ||
value: string; | ||
} | ||
|
||
/** | ||
* A material design chips input component working with chip list component. | ||
* To be placed inside md-form-field. Can be placed inside <md-chip-list> or outside <md-chip-list>. |
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.
Nit: Use < > with md-form-field, since we are using them with md-chip-list later in the comment
src/lib/chips/chip.ts
Outdated
} | ||
protected _selected: boolean = false; | ||
|
||
/** The value of the chip. Default to the content inside <md-chip> tags. */ |
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.
Typo: Default -> Defaults
src/lib/chips/chip-list.ts
Outdated
import {MdFormFieldControl} from '../form-field/form-field-control'; | ||
|
||
|
||
let nextUniqueId = 0; |
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.
Comment for this?
src/lib/chips/chip-list.ts
Outdated
|
||
protected _value: any; | ||
|
||
/** Placeholder for the chip list. Alternatively, placeholder can set to MdChipInput */ |
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.
Typo: can set to -> can be set on
src/lib/chips/chips.scss
Outdated
@@ -90,3 +91,8 @@ $mat-chips-chip-margin: 8px; | |||
margin-left: $mat-chip-remove-margin-after; | |||
} | |||
} | |||
|
|||
input.mat-chip-input { | |||
width: 150px; |
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.
Should this be a Sass variable?
src/lib/chips/chip-list.ts
Outdated
if (this._isValidIndex(chipIndex)) { | ||
if (chip._hasFocus) { | ||
// Check whether the chip is the last item | ||
if (chipIndex < this.chips.length - 1) { |
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.
Nit: we could clarify this comment. The check is really for whether the chip is not the last item, right? I had to read it a few times before I realized it was doing the opposite
src/lib/chips/chip-list.ts
Outdated
const isSubmitted = (this._parentFormGroup && this._parentFormGroup.submitted) || | ||
(this._parentForm && this._parentForm.submitted); | ||
|
||
return !!(isInvalid && (isTouched || isSubmitted)); |
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 looks pretty similar to the select's default error state here: https://github.com/angular/material2/blob/master/src/lib/select/select.ts#L678
Could we share it somewhere? There is a default errorStateMatcher - could we update it to add the control existence checks?
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.
We have #6741 to track sharing common code.
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, good to know, thanks.
src/lib/chips/chip-list.ts
Outdated
|
||
/** | ||
* Whether or not this chip is selectable. When a chip is not selectable, | ||
* it's selected state is always ignored. |
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.
Typo: it's -> its
src/lib/chips/chip-list.ts
Outdated
@Output() change: EventEmitter<MdChipListChange> = new EventEmitter<MdChipListChange>(); | ||
|
||
/** | ||
* Event that emits whenever the raw value of the select changes. This is here primarily |
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.
Typo: select -> chip list
// A different comparator means the selection could change. | ||
this._initializeSelection(); | ||
} | ||
} |
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.
Could we share more code with the select here? Maybe a mixin that adds compareWith and default compareWith?
Thanks for review. Comments addressed. Please take another look. Thanks! |
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.
@tinayuangao can you add the deprecated message to the commit message? That's what the changelog script picks up
Might be easiest to squash
Does chips.md
need to be updated at all for this change?
src/lib/chips/chip-input.ts
Outdated
/** | ||
* A material design chips input component working with chip list component. | ||
* To be placed inside <md-form-field>. Can be placed inside <md-chip-list> or outside | ||
* <md-chip-list>. |
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.
How about
Directive that adds chip-specific behaviors to an input element inside <md-form-field>.
May be placed inside or outside of an <md-chip-list>.
src/lib/chips/chip-input.ts
Outdated
* <md-chip>Chip 2<md-chip> | ||
* <input mdChipInputFor="chipList"> | ||
* </md-chip-list> | ||
* </md-form-field> |
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 it's better to leave the examples in the docs / live examples, since the generated API don't wont give this a nice visual treatment
'[attr.aria-describedby]': '_ariaDescribedby || null', | ||
'[attr.aria-required]': 'required.toString()', | ||
'[attr.aria-disabled]': 'disabled.toString()', | ||
'[attr.aria-invalid]': 'errorState', |
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.
Does the component support multiple selection? If not, it should have aria-multiselectable="false"
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.
Now it only support multiple selection.
I think I should change it to support single selection so we can share more common code with select component.
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.
Added single selection support to chip list. Please take another look. Thanks!
src/lib/chips/chip-list.ts
Outdated
/** Comparison function to specify which option is displayed. Defaults to object equality. */ | ||
private _compareWith = (o1: any, o2: any) => o1 === o2; | ||
|
||
get selected(): MdChip[] { |
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.
Add description?
src/lib/chips/chip-list.ts
Outdated
return this._chipInput ? this._chipInput.placeholder : this._placeholder; | ||
} | ||
|
||
/** For FormFieldControl. If any of the chips has focus, or the chip input has focus */ |
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.
/** Whether any chips or the mdChipInput inside of this chip-list has focus. */
src/lib/chips/chip-list.ts
Outdated
return (!this._chipInput || this._chipInput.empty) && this.chips.length === 0; | ||
} | ||
|
||
/** For FormFieldControl. The disabled is not depend on chip input */ |
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.
/** Whether this chip-list is disabled. */
src/lib/chips/chip-list.ts
Outdated
get disabled() { return this.ngControl ? this.ngControl.disabled : this._disabled; } | ||
set disabled(value: any) { this._disabled = coerceBooleanProperty(value); } | ||
|
||
get errorState(): 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.
Add description?
src/lib/chips/chip-list.ts
Outdated
} | ||
} | ||
|
||
_onBlur() { |
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.
It's weird that there's a _blur()
and an _onBlur()
- could the names be more specific? Function descriptions would also help
src/lib/chips/chip-list.ts
Outdated
if (!this.disabled) { | ||
if (this._chipInput) { | ||
// Check if focus moved to chip input. If not, do real on blur | ||
setTimeout(() => { |
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.
Add comment for why this needs to be a timeout?
src/lib/chips/chip-list.ts
Outdated
} | ||
|
||
_blur() { | ||
if (!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.
Would be good to have a comment that explains what this method is doing
dd2f0a0
to
234f0d2
Compare
Comments addressed. Updated |
0cccb44
to
96ca320
Compare
@tinayuangao prerender test looks to be failing now |
@jelbourn Fixed prerender test. Please take a look. Thanks! |
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, just needs rebase
8117d07
to
19f0842
Compare
Fixed focus & blur behavior for chip list. PTAL. Thanks! |
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
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
0416022
to
0d75c2a
Compare
0d75c2a
to
1bfa13e
Compare
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. |
Closes #5999 #6077
md-chip
's API similar tomd-option
md-chip-list
's API similar tomd-select
Breaking changes in
MdChip
:@Output() select = EventEmitter<MdChipEvent>
and@Output() deselect = EventEmitter<MdChipEvent>
@Output() onSelectionChange = EventEmitter<MdChipSelectionChange>