From 0d3ae16498cd7753243d8a30991401169c5d70e7 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 10 Jul 2018 22:07:15 +0200 Subject: [PATCH] feat(select): allow for option sorting logic to be customized Adds an input for a `sortComparator` to `mat-select`. The new input allows consumers to override the logic that `mat-select` uses to sort its values. Fixes #11871. --- src/lib/select/select.spec.ts | 27 ++++++++++++++++++++++++++- src/lib/select/select.ts | 16 +++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index be4159cd5c84..4ba61748e2ab 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -3837,6 +3837,29 @@ describe('MatSelect', () => { expect(fixture.componentInstance.control.value).toEqual(['steak-0', 'pizza-1', 'tacos-2']); })); + it('should be able to customize the value sorting logic', fakeAsync(() => { + fixture.componentInstance.sortComparator = (a, b, optionsArray) => { + return optionsArray.indexOf(b) - optionsArray.indexOf(a); + }; + fixture.detectChanges(); + + trigger.click(); + fixture.detectChanges(); + flush(); + + const options = overlayContainerElement.querySelectorAll('mat-option') as + NodeListOf; + + for (let i = 0; i < 3; i++) { + options[i].click(); + } + fixture.detectChanges(); + + // Expect the items to be in reverse order. + expect(trigger.textContent).toContain('Tacos, Pizza, Steak'); + expect(fixture.componentInstance.control.value).toEqual(['tacos-2', 'pizza-1', 'steak-0']); + })); + it('should sort the values that get set via the model based on the panel order', fakeAsync(() => { trigger.click(); @@ -4307,7 +4330,8 @@ class FloatLabelSelect { selector: 'multi-select', template: ` - + {{ food.viewValue }} @@ -4330,6 +4354,7 @@ class MultiSelect { @ViewChild(MatSelect) select: MatSelect; @ViewChildren(MatOption) options: QueryList; + sortComparator: (a: MatOption, b: MatOption, options: MatOption[]) => number; } @Component({ diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index cd11b499f569..d60bb6dc207d 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -382,7 +382,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, } /** - * A function to compare the option values with the selected values. The first argument + * Function to compare the option values with the selected values. The first argument * is a value from an option. The second is a value from the selection. A boolean * should be returned. */ @@ -416,9 +416,15 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, /** Input that can be used to specify the `aria-labelledby` attribute. */ @Input('aria-labelledby') ariaLabelledby: string; - /** An object used to control when error messages are shown. */ + /** Object used to control when error messages are shown. */ @Input() errorStateMatcher: ErrorStateMatcher; + /** + * Function used to sort the values in a select in multiple mode. + * Follows the same logic as `Array.prototype.sort`. + */ + @Input() sortComparator: (a: MatOption, b: MatOption, options: MatOption[]) => number; + /** Unique id of the element. */ @Input() get id(): string { return this._id; } @@ -920,7 +926,11 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, private _sortValues() { if (this.multiple) { const options = this.options.toArray(); - this._selectionModel.sort((a, b) => options.indexOf(a) - options.indexOf(b)); + + this._selectionModel.sort((a, b) => { + return this.sortComparator ? this.sortComparator(a, b, options) : + options.indexOf(a) - options.indexOf(b); + }); this.stateChanges.next(); } }