Skip to content

Commit c7645ee

Browse files
zakton5manucorporat
authored andcommitted
feat(select): add compareWith Input for object value comparison (#11965)
fixes #6625
1 parent 2743c63 commit c7645ee

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

demos/src/select/pages/page-one/page-one.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
</ion-select>
2121
</ion-item>
2222

23+
<ion-item>
24+
<ion-label>Hair Color</ion-label>
25+
<ion-select [(ngModel)]="hairColor" okText="Okay" cancelText="Dismiss" [compareWith]="compareFn">
26+
<ion-option *ngFor="let o of hairColorData" [value]="o">{{o.text}}</ion-option>
27+
</ion-select>
28+
</ion-item>
29+
2330
<ion-item>
2431
<ion-label>Gaming</ion-label>
2532
<ion-select [(ngModel)]="gaming" okText="Okay" cancelText="Dismiss">
@@ -147,6 +154,13 @@
147154
</ion-select>
148155
</ion-item>
149156

157+
<ion-item>
158+
<ion-label>Skittles</ion-label>
159+
<ion-select [(ngModel)]="skittles" multiple="true" okText="Okay" cancelText="Dismiss" [compareWith]="compareFn">
160+
<ion-option *ngFor="let o of skittlesData" [value]="o">{{o.text}}</ion-option>
161+
</ion-select>
162+
</ion-item>
163+
150164
<ion-item>
151165
<ion-label>Disabled</ion-label>
152166
<ion-select multiple disabled="true">

demos/src/select/pages/page-one/page-one.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export class PageOne {
1010
petAlertOpts: any;
1111
petData: any;
1212
pets: Array<string>;
13+
hairColorData: any;
14+
hairColor: any;
15+
skittlesData: any;
16+
skittles: Array<any>;
1317
notifications: string = 'mute_1';
1418
rating: number = 2;
1519

@@ -31,9 +35,37 @@ export class PageOne {
3135
{ text: 'Honey Badger', value: 'honeybadger' },
3236
];
3337

38+
this.hairColorData = [
39+
{ text: 'Brown', value: 'brown' },
40+
{ text: 'Blonde', value: 'blonde' },
41+
{ text: 'Black', value: 'black' },
42+
{ text: 'Red', value: 'red' }
43+
];
44+
45+
// Pre-selected object with different object reference
46+
this.hairColor = { text: 'Brown', value: 'brown' };
47+
48+
this.skittlesData = [
49+
{ text: 'Red', value: 'red' },
50+
{ text: 'Orange', value: 'orange' },
51+
{ text: 'Yellow', value: 'yellow' },
52+
{ text: 'Green', value: 'green' },
53+
{ text: 'Purple', value: 'purple' }
54+
];
55+
56+
// Pre-selected object with different object reference
57+
this.skittles = [
58+
{ text: 'Red', value: 'red' },
59+
{ text: 'Purple', value: 'purple' }
60+
];
61+
3462
this.pets = ['cat', 'dog'];
3563
}
3664

65+
compareFn(option1: any, option2: any) {
66+
return option1.value === option2.value;
67+
}
68+
3769
monthChange(val: any) {
3870
console.log('Month Change:', val);
3971
}

src/components/select/select.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,31 @@ import { SelectPopover, SelectPopoverOption } from './select-popover-component';
123123
* };
124124
* ```
125125
*
126+
* ### Object Value References
127+
*
128+
* When using objects for select values, it is possible for the identities of these objects to
129+
* change if they are coming from a server or database, while the selected value's identity
130+
* remains the same. For example, this can occur when an existing record with the desired object value
131+
* is loaded into the select, but the newly retrieved select options now have different identities. This will
132+
* result in the select appearing to have no value at all, even though the original selection in still intact.
133+
*
134+
* Using the `compareWith` `Input` is the solution to this problem
135+
*
136+
* ```html
137+
* <ion-item>
138+
* <ion-label>Employee</ion-label>
139+
* <ion-select [(ngModel)]="employee" [compareWith]="compareFn">
140+
* <ion-option *ngFor="let employee of employees" [value]="employee">{{employee.name}}</ion-option>
141+
* </ion-select>
142+
* </ion-item>
143+
* ```
144+
*
145+
* ```ts
146+
* compareFn(e1: Employee, e2: Employee): boolean {
147+
* return e1 && e2 ? e1.id === e2.id : e1 === e2;
148+
* }
149+
* ```
150+
*
126151
* @demo /docs/demos/src/select/
127152
*/
128153
@Component({
@@ -153,6 +178,7 @@ export class Select extends BaseInput<any> implements OnDestroy {
153178
_overlay: ActionSheet | Alert | Popover;
154179
_texts: string[] = [];
155180
_text: string = '';
181+
_compareWith: (o1: any, o2: any) => boolean = isCheckedProperty;
156182

157183
/**
158184
* @input {string} The text to display on the cancel button. Default: `Cancel`.
@@ -187,6 +213,18 @@ export class Select extends BaseInput<any> implements OnDestroy {
187213
*/
188214
@Input() selectedText: string = '';
189215

216+
/**
217+
* @input {Function} The function that will be called to compare object values
218+
*/
219+
@Input()
220+
set compareWith(fn: (o1: any, o2: any) => boolean) {
221+
if (typeof fn !== 'function') {
222+
throw new Error(`compareWith must be a function, but received ${JSON.stringify(fn)}`);
223+
}
224+
this._compareWith = fn;
225+
}
226+
227+
190228
/**
191229
* @output {any} Emitted when the selection was cancelled.
192230
*/
@@ -448,7 +486,7 @@ export class Select extends BaseInput<any> implements OnDestroy {
448486
this._options.forEach(option => {
449487
// check this option if the option's value is in the values array
450488
option.selected = this.getValues().some(selectValue => {
451-
return isCheckedProperty(selectValue, option.value);
489+
return this._compareWith(selectValue, option.value);
452490
});
453491

454492
if (option.selected) {

0 commit comments

Comments
 (0)