refactor(): remove checked property in favor of parent value (#19449)

BREAKING CHANGE:


The following components have been updated to remove the checked or selected properties:

- Radio
- Segment Button
- Select

Developers should set the value property on the respective parent components in order to managed checked/selected status.

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
This commit is contained in:
Manu MA
2020-01-17 23:22:26 +01:00
committed by Liam DeBeasi
parent 9232f16eea
commit a5229d90ca
52 changed files with 879 additions and 939 deletions

View File

@@ -4,8 +4,7 @@ Selects are form controls to select an option, or options, from a set of options
A select should be used with child `<ion-select-option>` elements. If the child option is not given a `value` attribute then its text will be used as the value.
If `value` is set on the `<ion-select>`, the selected option will be chosen based on that value. Otherwise, the `selected` attribute can be used on the `<ion-select-option>`.
If `value` is set on the `<ion-select>`, the selected option will be chosen based on that value.
## Interfaces
@@ -110,10 +109,10 @@ Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert
<ion-item>
<ion-label>Pets</ion-label>
<ion-select multiple="true">
<ion-select-option value="bird" selected>Bird</ion-select-option>
<ion-select multiple="true" [value]="['bird', 'dog']">
<ion-select-option value="bird">Bird</ion-select-option>
<ion-select-option value="cat">Cat</ion-select-option>
<ion-select-option value="dog" selected>Dog</ion-select-option>
<ion-select-option value="dog">Dog</ion-select-option>
<ion-select-option value="honeybadger">Honey Badger</ion-select-option>
</ion-select>
</ion-item>
@@ -315,16 +314,21 @@ export class SelectExample {
<ion-item>
<ion-label>Pets</ion-label>
<ion-select multiple="true">
<ion-select-option value="bird" selected>Bird</ion-select-option>
<ion-select id="multiple" multiple="true">
<ion-select-option value="bird">Bird</ion-select-option>
<ion-select-option value="cat">Cat</ion-select-option>
<ion-select-option value="dog" selected>Dog</ion-select-option>
<ion-select-option value="dog">Dog</ion-select-option>
<ion-select-option value="honeybadger">Honey Badger</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
```
```javascript
const select = document.querySelector('multiple');
select.value = ['bird', 'dog'];
```
## Objects as Values
```html
@@ -372,10 +376,11 @@ export class SelectExample {
let selectOption = document.createElement('ion-select-option');
selectOption.value = option;
selectOption.textContent = option.first + ' ' + option.last;
selectOption.selected = (i === 0);
objectSelectElement.appendChild(selectOption)
});
objectSelectElement.value = objectOptions[0];
}
```
@@ -564,12 +569,12 @@ export const SelectExample: React.FC = () => (
<IonItem>
<IonLabel>Pets</IonLabel>
<IonSelect multiple={true}>
<IonSelectOption value="bird" selected>
<IonSelect multiple={true} value={['bird', 'dog']}>
<IonSelectOption value="bird">
Bird
</IonSelectOption>
<IonSelectOption value="cat">Cat</IonSelectOption>
<IonSelectOption value="dog" selected>
<IonSelectOption value="dog">
Dog
</IonSelectOption>
<IonSelectOption value="honeybadger">Honey Badger</IonSelectOption>
@@ -719,10 +724,10 @@ export const SelectExample: React.FC = () => (
<ion-item>
<ion-label>Pets</ion-label>
<ion-select multiple="true">
<ion-select-option value="bird" selected>Bird</ion-select-option>
<ion-select multiple="true" :value=['bird', 'dog']>
<ion-select-option value="bird">Bird</ion-select-option>
<ion-select-option value="cat">Cat</ion-select-option>
<ion-select-option value="dog" selected>Dog</ion-select-option>
<ion-select-option value="dog">Dog</ion-select-option>
<ion-select-option value="honeybadger">Honey Badger</ion-select-option>
</ion-select>
</ion-item>
@@ -769,7 +774,7 @@ export const SelectExample: React.FC = () => (
<ion-item>
<ion-label>Action Sheet</ion-label>
<ion-select :interfaceOptions]="customActionSheetOptions" interface="action-sheet" placeholder="Select One">
<ion-select :interfaceOptions="customActionSheetOptions" interface="action-sheet" placeholder="Select One">
<ion-select-option value="red">Red</ion-select-option>
<ion-select-option value="purple">Purple</ion-select-option>
<ion-select-option value="yellow">Yellow</ion-select-option>

View File

@@ -125,7 +125,6 @@ export class Select implements ComponentInterface {
@Watch('value')
valueChanged() {
this.updateOptions();
this.emitStyle();
if (this.didInit) {
this.ionChange.emit({
@@ -135,25 +134,10 @@ export class Select implements ComponentInterface {
}
async connectedCallback() {
if (this.value === undefined) {
if (this.multiple) {
// there are no values set at this point
// so check to see who should be selected
const checked = this.childOpts.filter(o => o.selected);
this.value = checked.map(o => getOptionValue(o));
} else {
const checked = this.childOpts.find(o => o.selected);
if (checked) {
this.value = getOptionValue(checked);
}
}
}
this.updateOptions();
this.updateOverlayOptions();
this.emitStyle();
this.mutationO = watchForOptions<HTMLIonSelectOptionElement>(this.el, 'ion-select-option', async () => {
this.updateOptions();
this.updateOverlayOptions();
});
}
@@ -218,30 +202,32 @@ export class Select implements ComponentInterface {
return;
}
const childOpts = this.childOpts;
const value = this.value;
switch (this.interface) {
case 'action-sheet':
overlay.buttons = this.createActionSheetButtons(childOpts);
overlay.buttons = this.createActionSheetButtons(childOpts, value);
break;
case 'popover':
const popover = overlay.querySelector('ion-select-popover');
if (popover) {
popover.options = this.createPopoverOptions(childOpts);
popover.options = this.createPopoverOptions(childOpts, value);
}
break;
case 'alert':
const inputType = (this.multiple ? 'checkbox' : 'radio');
overlay.inputs = this.createAlertInputs(childOpts, inputType);
overlay.inputs = this.createAlertInputs(childOpts, inputType, value);
break;
}
}
private createActionSheetButtons(data: any[]): ActionSheetButton[] {
private createActionSheetButtons(data: HTMLIonSelectOptionElement[], selectValue: any): ActionSheetButton[] {
const actionSheetButtons = data.map(option => {
const value = getOptionValue(option);
return {
role: (option.selected ? 'selected' : ''),
role: (isOptionSelected(value, selectValue, this.compareWith) ? 'selected' : ''),
text: option.textContent,
handler: () => {
this.value = getOptionValue(option);
this.value = value;
}
} as ActionSheetButton;
});
@@ -258,38 +244,39 @@ export class Select implements ComponentInterface {
return actionSheetButtons;
}
private createAlertInputs(data: any[], inputType: string): AlertInput[] {
return data.map(o => {
return {
type: inputType,
label: o.textContent,
value: getOptionValue(o),
checked: o.selected,
disabled: o.disabled
} as AlertInput;
});
}
private createPopoverOptions(data: any[]): SelectPopoverOption[] {
private createAlertInputs(data: HTMLIonSelectOptionElement[], inputType: 'checkbox' | 'radio', selectValue: any): AlertInput[] {
return data.map(o => {
const value = getOptionValue(o);
return {
text: o.textContent,
type: inputType,
label: o.textContent || '',
value,
checked: o.selected,
checked: isOptionSelected(value, selectValue, this.compareWith),
disabled: o.disabled
};
});
}
private createPopoverOptions(data: HTMLIonSelectOptionElement[], selectValue: any): SelectPopoverOption[] {
return data.map(o => {
const value = getOptionValue(o);
return {
text: o.textContent || '',
value,
checked: isOptionSelected(value, selectValue, this.compareWith),
disabled: o.disabled,
handler: () => {
this.value = value;
this.close();
}
} as SelectPopoverOption;
};
});
}
private async openPopover(ev: UIEvent) {
const interfaceOptions = this.interfaceOptions;
const mode = getIonMode(this);
const value = this.value;
const popoverOpts: PopoverOptions = {
mode,
...interfaceOptions,
@@ -301,22 +288,21 @@ export class Select implements ComponentInterface {
header: interfaceOptions.header,
subHeader: interfaceOptions.subHeader,
message: interfaceOptions.message,
value: this.value,
options: this.createPopoverOptions(this.childOpts)
value,
options: this.createPopoverOptions(this.childOpts, value)
}
};
return popoverController.create(popoverOpts);
}
private async openActionSheet() {
const mode = getIonMode(this);
const interfaceOptions = this.interfaceOptions;
const actionSheetOpts: ActionSheetOptions = {
mode,
...interfaceOptions,
buttons: this.createActionSheetButtons(this.childOpts),
buttons: this.createActionSheetButtons(this.childOpts, this.value),
cssClass: ['select-action-sheet', interfaceOptions.cssClass]
};
return actionSheetController.create(actionSheetOpts);
@@ -335,7 +321,7 @@ export class Select implements ComponentInterface {
...interfaceOptions,
header: interfaceOptions.header ? interfaceOptions.header : labelText,
inputs: this.createAlertInputs(this.childOpts, inputType),
inputs: this.createAlertInputs(this.childOpts, inputType, this.value),
buttons: [
{
text: this.cancelText,
@@ -368,23 +354,6 @@ export class Select implements ComponentInterface {
return this.overlay.dismiss();
}
private updateOptions() {
// iterate all options, updating the selected prop
let canSelect = true;
const { value, childOpts, compareWith, multiple } = this;
for (const selectOption of childOpts) {
const optValue = getOptionValue(selectOption);
const selected = canSelect && isOptionSelected(value, optValue, compareWith);
selectOption.selected = selected;
// if current option is selected and select is single-option, we can't select
// any option more
if (selected && !multiple) {
canSelect = false;
}
}
}
private getLabel() {
return findItemLabel(this.el);
}
@@ -490,6 +459,17 @@ export class Select implements ComponentInterface {
}
}
const isOptionSelected = (currentValue: any[] | any, compareValue: any, compareWith?: string | SelectCompareFn | null) => {
if (currentValue === undefined) {
return false;
}
if (Array.isArray(currentValue)) {
return currentValue.some(val => compareOptions(val, compareValue, compareWith));
} else {
return compareOptions(currentValue, compareValue, compareWith);
}
};
const getOptionValue = (el: HTMLIonSelectOptionElement) => {
const value = el.value;
return (value === undefined)
@@ -507,24 +487,13 @@ const parseValue = (value: any) => {
return value.toString();
};
const isOptionSelected = (currentValue: any[] | any, compareValue: any, compareWith?: string | SelectCompareFn | null) => {
if (currentValue === undefined) {
return false;
}
if (Array.isArray(currentValue)) {
return currentValue.some(val => compareOptions(val, compareValue, compareWith));
} else {
return compareOptions(currentValue, compareValue, compareWith);
}
};
const compareOptions = (currentValue: any, compareValue: any, compareWith?: string | SelectCompareFn | null): boolean => {
if (typeof compareWith === 'function') {
return compareWith(currentValue, compareValue);
} else if (typeof compareWith === 'string') {
return currentValue[compareWith] === compareValue[compareWith];
} else {
return currentValue === compareValue;
return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;
}
};

View File

@@ -39,7 +39,7 @@
<ion-item>
<ion-label>Hair Color</ion-label>
<ion-select id="hairColor" value="brown" ok-text="Okay" cancel-text="Dismiss">
<ion-select-option value="brown" selected>Brown</ion-select-option>
<ion-select-option value="brown">Brown</ion-select-option>
<ion-select-option value="blonde">Blonde</ion-select-option>
<ion-select-option value="black">Black</ion-select-option>
<ion-select-option value="red">Red</ion-select-option>
@@ -81,8 +81,8 @@
<ion-item color="danger">
<ion-label>Alert</ion-label>
<ion-select id="customAlertSelect" interface="alert" multiple="true" placeholder="Select One">
<ion-select-option value="bacon" selected>Bacon</ion-select-option>
<ion-select id="customAlertSelect" value="bacon" interface="alert" multiple="true" placeholder="Select One">
<ion-select-option value="bacon">Bacon</ion-select-option>
<ion-select-option value="olives">Black Olives</ion-select-option>
<ion-select-option value="xcheese">Extra Cheese</ion-select-option>
<ion-select-option value="peppers">Green Peppers</ion-select-option>
@@ -147,10 +147,10 @@
<ion-item>
<ion-label>Date</ion-label>
<ion-select placeholder="Month" interface="popover">
<ion-select placeholder="Month" value="03" interface="popover">
<ion-select-option value="01">January</ion-select-option>
<ion-select-option value="02">February</ion-select-option>
<ion-select-option value="03" selected="true">March</ion-select-option>
<ion-select-option value="03">March</ion-select-option>
<ion-select-option value="04">April</ion-select-option>
<ion-select-option value="05">May</ion-select-option>
<ion-select-option value="06">June</ion-select-option>
@@ -161,18 +161,18 @@
<ion-select-option value="11">November</ion-select-option>
<ion-select-option value="12">December</ion-select-option>
</ion-select>
<ion-select placeholder="Year" interface="popover">
<ion-select-option>1989</ion-select-option>
<ion-select-option>1990</ion-select-option>
<ion-select-option>1991</ion-select-option>
<ion-select-option>1992</ion-select-option>
<ion-select-option>1993</ion-select-option>
<ion-select-option selected="true">1994</ion-select-option>
<ion-select-option>1995</ion-select-option>
<ion-select-option>1996</ion-select-option>
<ion-select-option>1997</ion-select-option>
<ion-select-option>1998</ion-select-option>
<ion-select-option>1999</ion-select-option>
<ion-select placeholder="Year" value="94" interface="popover">
<ion-select-option value="89">1989</ion-select-option>
<ion-select-option value="90">1990</ion-select-option>
<ion-select-option value="91">1991</ion-select-option>
<ion-select-option value="92">1992</ion-select-option>
<ion-select-option value="93">1993</ion-select-option>
<ion-select-option value="94">1994</ion-select-option>
<ion-select-option value="95">1995</ion-select-option>
<ion-select-option value="96">1996</ion-select-option>
<ion-select-option value="97">1997</ion-select-option>
<ion-select-option value="98">1998</ion-select-option>
<ion-select-option value="99">1999</ion-select-option>
</ion-select>
</ion-item>
@@ -256,8 +256,8 @@
<ion-item>
<ion-label>Disabled</ion-label>
<ion-select multiple disabled="true">
<ion-select-option selected="true">Selected Text</ion-select-option>
<ion-select multiple value="text" disabled="true">
<ion-select-option value="text">Selected Text</ion-select-option>
</ion-select>
</ion-item>
@@ -272,10 +272,10 @@
<ion-item>
<ion-label position="stacked">Stacked: selected</ion-label>
<ion-select>
<ion-select-option>Default</ion-select-option>
<ion-select-option selected>Other</ion-select-option>
<ion-select-option>N/A</ion-select-option>
<ion-select value="other">
<ion-select-option value="default">Default</ion-select-option>
<ion-select-option value="other">Other</ion-select-option>
<ion-select-option value="na">N/A</ion-select-option>
</ion-select>
</ion-item>
@@ -290,10 +290,10 @@
<ion-item>
<ion-label position="floating">Floating: selected</ion-label>
<ion-select>
<ion-select-option selected="true">Default</ion-select-option>
<ion-select-option>Other</ion-select-option>
<ion-select-option>N/A</ion-select-option>
<ion-select value="default">
<ion-select-option value="default">Default</ion-select-option>
<ion-select-option value="other">Other</ion-select-option>
<ion-select-option value="na">N/A</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
@@ -345,10 +345,15 @@
let selectOption = document.createElement('ion-select-option');
selectOption.value = option;
selectOption.textContent = option.first + ' ' + option.last;
selectOption.selected = (i === 0);
objectSelectElement.appendChild(selectOption)
});
objectSelectElement.value = {
id: 1,
first: 'Alice',
last: 'Smith',
};
setTimeout(() => {
objectSelectElement.value = {

View File

@@ -32,9 +32,9 @@
<ion-item>
<ion-label position="floating">Hair Color</ion-label>
<ion-select>
<ion-select value="">
<ion-select-option value="brown">Brown</ion-select-option>
<ion-select-option value="" selected>Empty Value</ion-select-option>
<ion-select-option value="">Empty Value</ion-select-option>
<ion-select-option value="black">Black</ion-select-option>
<ion-select-option value="red">Red</ion-select-option>
</ion-select>

View File

@@ -62,15 +62,15 @@
<ion-item>
<ion-label>Disabled</ion-label>
<ion-select id="disabled" multiple disabled>
<ion-select-option selected="true">Selected Text</ion-select-option>
<ion-select id="disabled" multiple disabled value="selected">
<ion-select-option value="selected">Selected Text</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label>Statuses</ion-label>
<ion-select id="status" multiple>
<ion-select-option value="selected" selected="true">Selected</ion-select-option>
<ion-select id="status" value="selected" multiple>
<ion-select-option value="selected">Selected</ion-select-option>
<ion-select-option value="default">Default</ion-select-option>
<ion-select-option value="disabled" disabled="true">Disabled</ion-select-option>
</ion-select>
@@ -92,10 +92,10 @@
</ion-item>
<ion-item>
<ion-label>Select</ion-label>
<ion-select multiple="true">
<ion-select-option selected>1</ion-select-option>
<ion-select-option>2</ion-select-option>
<ion-select-option selected>3</ion-select-option>
<ion-select id="multiple" multiple="true">
<ion-select-option value="1">1</ion-select-option>
<ion-select-option value="2">2</ion-select-option>
<ion-select-option value="3">3</ion-select-option>
</ion-select>
</ion-item>
<ion-button expand="block" type="submit">Submit</ion-button>
@@ -157,6 +157,9 @@
}
}
}
const multiple = document.querySelector('#multiple');
multiple.value = ['1', '3'];
</script>
</ion-app>
</body>

View File

@@ -126,8 +126,8 @@
<ion-item>
<ion-label>Statuses</ion-label>
<ion-select id="status">
<ion-select-option value="selected" selected>Selected</ion-select-option>
<ion-select id="status" value="selected">
<ion-select-option value="selected">Selected</ion-select-option>
<ion-select-option value="default">Default</ion-select-option>
<ion-select-option value="disabled" disabled="true">Disabled</ion-select-option>
</ion-select>

View File

@@ -22,8 +22,8 @@
<ion-select-option value="m">Male</ion-select-option>
</ion-select>
<ion-select id="hairColor" ok-text="Okay" cancel-text="Dismiss">
<ion-select-option value="brown" selected>Brown</ion-select-option>
<ion-select id="hairColor" ok-text="Okay" value="brown" cancel-text="Dismiss">
<ion-select-option value="brown">Brown</ion-select-option>
<ion-select-option value="blonde">Blonde</ion-select-option>
<ion-select-option value="black">Black</ion-select-option>
<ion-select-option value="red">Red</ion-select-option>
@@ -39,10 +39,10 @@
<ion-select-option value="snes">SNES</ion-select-option>
</ion-select>
<ion-select placeholder="Month">
<ion-select placeholder="Month" value="03">
<ion-select-option value="01">January</ion-select-option>
<ion-select-option value="02">February</ion-select-option>
<ion-select-option value="03" selected="true">March</ion-select-option>
<ion-select-option value="03">March</ion-select-option>
<ion-select-option value="04">April</ion-select-option>
<ion-select-option value="05">May</ion-select-option>
<ion-select-option value="06">June</ion-select-option>
@@ -54,18 +54,18 @@
<ion-select-option value="12">December</ion-select-option>
</ion-select>
<ion-select placeholder="Year">
<ion-select-option>1989</ion-select-option>
<ion-select-option>1990</ion-select-option>
<ion-select-option>1991</ion-select-option>
<ion-select-option>1992</ion-select-option>
<ion-select-option>1993</ion-select-option>
<ion-select-option selected="true">1994</ion-select-option>
<ion-select-option>1995</ion-select-option>
<ion-select-option>1996</ion-select-option>
<ion-select-option>1997</ion-select-option>
<ion-select-option>1998</ion-select-option>
<ion-select-option>1999</ion-select-option>
<ion-select placeholder="Year" value="94">
<ion-select-option value="89">1989</ion-select-option>
<ion-select-option value="90">1990</ion-select-option>
<ion-select-option value="91">1991</ion-select-option>
<ion-select-option value="92">1992</ion-select-option>
<ion-select-option value="93">1993</ion-select-option>
<ion-select-option value="94">1994</ion-select-option>
<ion-select-option value="95">1995</ion-select-option>
<ion-select-option value="96">1996</ion-select-option>
<ion-select-option value="97">1997</ion-select-option>
<ion-select-option value="98">1998</ion-select-option>
<ion-select-option value="99">1999</ion-select-option>
</ion-select>
<ion-select class="custom" placeholder="Day">

View File

@@ -57,10 +57,10 @@
<ion-item>
<ion-label>Pets</ion-label>
<ion-select multiple="true">
<ion-select-option value="bird" selected>Bird</ion-select-option>
<ion-select multiple="true" [value]="['bird', 'dog']">
<ion-select-option value="bird">Bird</ion-select-option>
<ion-select-option value="cat">Cat</ion-select-option>
<ion-select-option value="dog" selected>Dog</ion-select-option>
<ion-select-option value="dog">Dog</ion-select-option>
<ion-select-option value="honeybadger">Honey Badger</ion-select-option>
</ion-select>
</ion-item>

View File

@@ -57,16 +57,21 @@
<ion-item>
<ion-label>Pets</ion-label>
<ion-select multiple="true">
<ion-select-option value="bird" selected>Bird</ion-select-option>
<ion-select id="multiple" multiple="true">
<ion-select-option value="bird">Bird</ion-select-option>
<ion-select-option value="cat">Cat</ion-select-option>
<ion-select-option value="dog" selected>Dog</ion-select-option>
<ion-select-option value="dog">Dog</ion-select-option>
<ion-select-option value="honeybadger">Honey Badger</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
```
```javascript
const select = document.querySelector('multiple');
select.value = ['bird', 'dog'];
```
## Objects as Values
```html
@@ -114,10 +119,11 @@
let selectOption = document.createElement('ion-select-option');
selectOption.value = option;
selectOption.textContent = option.first + ' ' + option.last;
selectOption.selected = (i === 0);
objectSelectElement.appendChild(selectOption)
});
objectSelectElement.value = objectOptions[0];
}
```

View File

@@ -104,12 +104,12 @@ export const SelectExample: React.FC = () => (
<IonItem>
<IonLabel>Pets</IonLabel>
<IonSelect multiple={true}>
<IonSelectOption value="bird" selected>
<IonSelect multiple={true} value={['bird', 'dog']}>
<IonSelectOption value="bird">
Bird
</IonSelectOption>
<IonSelectOption value="cat">Cat</IonSelectOption>
<IonSelectOption value="dog" selected>
<IonSelectOption value="dog">
Dog
</IonSelectOption>
<IonSelectOption value="honeybadger">Honey Badger</IonSelectOption>

View File

@@ -60,10 +60,10 @@
<ion-item>
<ion-label>Pets</ion-label>
<ion-select multiple="true">
<ion-select-option value="bird" selected>Bird</ion-select-option>
<ion-select multiple="true" :value=['bird', 'dog']>
<ion-select-option value="bird">Bird</ion-select-option>
<ion-select-option value="cat">Cat</ion-select-option>
<ion-select-option value="dog" selected>Dog</ion-select-option>
<ion-select-option value="dog">Dog</ion-select-option>
<ion-select-option value="honeybadger">Honey Badger</ion-select-option>
</ion-select>
</ion-item>
@@ -110,7 +110,7 @@
<ion-item>
<ion-label>Action Sheet</ion-label>
<ion-select :interfaceOptions]="customActionSheetOptions" interface="action-sheet" placeholder="Select One">
<ion-select :interfaceOptions="customActionSheetOptions" interface="action-sheet" placeholder="Select One">
<ion-select-option value="red">Red</ion-select-option>
<ion-select-option value="purple">Purple</ion-select-option>
<ion-select-option value="yellow">Yellow</ion-select-option>