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(); } }