Skip to content

Commit 7747e66

Browse files
crisbetommalerba
authored andcommitted
feat(selection-list): add support for compareWith function (#10501)
Similarly to `mat-select`, these changes add an input for a `compareWith` function to the selection list. The input allows consumers to set some custom logic for determining which options is selected, which is useful for cases where the value of a list option is an entire object.
1 parent 540226c commit 7747e66

File tree

2 files changed

+57
-7
lines changed

2 files changed

+57
-7
lines changed

src/lib/list/selection-list.spec.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import {
55
dispatchEvent,
66
dispatchKeyboardEvent,
77
} from '@angular/cdk/testing';
8-
import {Component, DebugElement, ChangeDetectionStrategy} from '@angular/core';
8+
import {
9+
Component,
10+
DebugElement,
11+
ChangeDetectionStrategy,
12+
QueryList,
13+
ViewChildren,
14+
} from '@angular/core';
915
import {async, ComponentFixture, fakeAsync, TestBed, tick, flush} from '@angular/core/testing';
1016
import {By} from '@angular/platform-browser';
1117
import {
@@ -620,6 +626,7 @@ describe('MatSelectionList with forms', () => {
620626
SelectionListWithPreselectedOption,
621627
SelectionListWithPreselectedOptionAndModel,
622628
SelectionListWithPreselectedFormControlOnPush,
629+
SelectionListWithCustomComparator,
623630
]
624631
});
625632

@@ -842,6 +849,24 @@ describe('MatSelectionList with forms', () => {
842849
}));
843850

844851
});
852+
853+
describe('with custom compare function', () => {
854+
it('should use a custom comparator to determine which options are selected', fakeAsync(() => {
855+
const fixture = TestBed.createComponent(SelectionListWithCustomComparator);
856+
const testComponent = fixture.componentInstance;
857+
858+
testComponent.compareWith = jasmine.createSpy('comparator', (o1, o2) => {
859+
return o1 && o2 && o1.id === o2.id;
860+
}).and.callThrough();
861+
862+
testComponent.selectedOptions = [{id: 2, label: 'Two'}];
863+
fixture.detectChanges();
864+
tick();
865+
866+
expect(testComponent.compareWith).toHaveBeenCalled();
867+
expect(testComponent.optionInstances.toArray()[1].selected).toBe(true);
868+
}));
869+
});
845870
});
846871

847872

@@ -1004,3 +1029,23 @@ class SelectionListWithPreselectedFormControlOnPush {
10041029
opts = ['opt1', 'opt2', 'opt3'];
10051030
formControl = new FormControl(['opt2']);
10061031
}
1032+
1033+
1034+
@Component({
1035+
template: `
1036+
<mat-selection-list [(ngModel)]="selectedOptions" [compareWith]="compareWith">
1037+
<mat-list-option *ngFor="let option of options" [value]="option">
1038+
{{option.label}}
1039+
</mat-list-option>
1040+
</mat-selection-list>`
1041+
})
1042+
class SelectionListWithCustomComparator {
1043+
@ViewChildren(MatListOption) optionInstances: QueryList<MatListOption>;
1044+
selectedOptions: {id: number, label: string}[] = [];
1045+
compareWith?: (o1: any, o2: any) => boolean;
1046+
options = [
1047+
{id: 1, label: 'One'},
1048+
{id: 2, label: 'Two'},
1049+
{id: 3, label: 'Three'}
1050+
];
1051+
}

src/lib/list/selection-list.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
279279
/** Tabindex of the selection list. */
280280
@Input() tabIndex: number = 0;
281281

282+
/**
283+
* Function used for comparing an option against the selected value when determining which
284+
* options should appear as selected. The first argument is the value of an options. The second
285+
* one is a value from the selected value. A boolean must be returned.
286+
*/
287+
@Input() compareWith: (o1: any, o2: any) => boolean;
288+
282289
/** The currently selected options. */
283290
selectedOptions: SelectionModel<MatListOption> = new SelectionModel<MatListOption>(true);
284291

@@ -437,17 +444,15 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
437444
this._onTouched = fn;
438445
}
439446

440-
/** Returns the option with the specified value. */
441-
private _getOptionByValue(value: string): MatListOption | undefined {
442-
return this.options.find(option => option.value === value);
443-
}
444-
445447
/** Sets the selected options based on the specified values. */
446448
private _setOptionsFromValues(values: string[]) {
447449
this.options.forEach(option => option._setSelected(false));
448450

449451
values
450-
.map(value => this._getOptionByValue(value))
452+
.map(value => {
453+
return this.options.find(option =>
454+
this.compareWith ? this.compareWith(option.value, value) : option.value === value);
455+
})
451456
.filter(Boolean)
452457
.forEach(option => option!._setSelected(true));
453458
}

0 commit comments

Comments
 (0)