From 7747e66ed4e97cf5cce37a864cd9a7cb71824f17 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 19 Apr 2018 11:44:31 -0600 Subject: [PATCH] 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. --- src/lib/list/selection-list.spec.ts | 47 ++++++++++++++++++++++++++++- src/lib/list/selection-list.ts | 17 +++++++---- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/lib/list/selection-list.spec.ts b/src/lib/list/selection-list.spec.ts index 2315e69e03f..d76e7de7aac 100644 --- a/src/lib/list/selection-list.spec.ts +++ b/src/lib/list/selection-list.spec.ts @@ -5,7 +5,13 @@ import { dispatchEvent, dispatchKeyboardEvent, } from '@angular/cdk/testing'; -import {Component, DebugElement, ChangeDetectionStrategy} from '@angular/core'; +import { + Component, + DebugElement, + ChangeDetectionStrategy, + QueryList, + ViewChildren, +} from '@angular/core'; import {async, ComponentFixture, fakeAsync, TestBed, tick, flush} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import { @@ -620,6 +626,7 @@ describe('MatSelectionList with forms', () => { SelectionListWithPreselectedOption, SelectionListWithPreselectedOptionAndModel, SelectionListWithPreselectedFormControlOnPush, + SelectionListWithCustomComparator, ] }); @@ -842,6 +849,24 @@ describe('MatSelectionList with forms', () => { })); }); + + describe('with custom compare function', () => { + it('should use a custom comparator to determine which options are selected', fakeAsync(() => { + const fixture = TestBed.createComponent(SelectionListWithCustomComparator); + const testComponent = fixture.componentInstance; + + testComponent.compareWith = jasmine.createSpy('comparator', (o1, o2) => { + return o1 && o2 && o1.id === o2.id; + }).and.callThrough(); + + testComponent.selectedOptions = [{id: 2, label: 'Two'}]; + fixture.detectChanges(); + tick(); + + expect(testComponent.compareWith).toHaveBeenCalled(); + expect(testComponent.optionInstances.toArray()[1].selected).toBe(true); + })); + }); }); @@ -1004,3 +1029,23 @@ class SelectionListWithPreselectedFormControlOnPush { opts = ['opt1', 'opt2', 'opt3']; formControl = new FormControl(['opt2']); } + + +@Component({ + template: ` + + + {{option.label}} + + ` +}) +class SelectionListWithCustomComparator { + @ViewChildren(MatListOption) optionInstances: QueryList; + selectedOptions: {id: number, label: string}[] = []; + compareWith?: (o1: any, o2: any) => boolean; + options = [ + {id: 1, label: 'One'}, + {id: 2, label: 'Two'}, + {id: 3, label: 'Three'} + ]; +} diff --git a/src/lib/list/selection-list.ts b/src/lib/list/selection-list.ts index 934fc41fe54..34843721688 100644 --- a/src/lib/list/selection-list.ts +++ b/src/lib/list/selection-list.ts @@ -279,6 +279,13 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu /** Tabindex of the selection list. */ @Input() tabIndex: number = 0; + /** + * Function used for comparing an option against the selected value when determining which + * options should appear as selected. The first argument is the value of an options. The second + * one is a value from the selected value. A boolean must be returned. + */ + @Input() compareWith: (o1: any, o2: any) => boolean; + /** The currently selected options. */ selectedOptions: SelectionModel = new SelectionModel(true); @@ -437,17 +444,15 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu this._onTouched = fn; } - /** Returns the option with the specified value. */ - private _getOptionByValue(value: string): MatListOption | undefined { - return this.options.find(option => option.value === value); - } - /** Sets the selected options based on the specified values. */ private _setOptionsFromValues(values: string[]) { this.options.forEach(option => option._setSelected(false)); values - .map(value => this._getOptionByValue(value)) + .map(value => { + return this.options.find(option => + this.compareWith ? this.compareWith(option.value, value) : option.value === value); + }) .filter(Boolean) .forEach(option => option!._setSelected(true)); }