Skip to content

Commit a3c404f

Browse files
committed
feat(select): allow for option sorting logic to be customized
Adds an input for a `sortPredicate` to `mat-select`. The new input allows consumers to override the logic that `mat-select` uses to sort its values. Fixes #11871.
1 parent c276e26 commit a3c404f

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

src/lib/select/select.spec.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3802,6 +3802,29 @@ describe('MatSelect', () => {
38023802
expect(fixture.componentInstance.control.value).toEqual(['steak-0', 'pizza-1', 'tacos-2']);
38033803
}));
38043804

3805+
it('should be able to customize the value sorting logic', fakeAsync(() => {
3806+
fixture.componentInstance.sortPredicate = (a, b, optionsArray) => {
3807+
return optionsArray.indexOf(b) - optionsArray.indexOf(a);
3808+
};
3809+
fixture.detectChanges();
3810+
3811+
trigger.click();
3812+
fixture.detectChanges();
3813+
flush();
3814+
3815+
const options = overlayContainerElement.querySelectorAll('mat-option') as
3816+
NodeListOf<HTMLElement>;
3817+
3818+
for (let i = 0; i < 3; i++) {
3819+
options[i].click();
3820+
}
3821+
fixture.detectChanges();
3822+
3823+
// Expect the items to be in reverse order.
3824+
expect(trigger.textContent).toContain('Tacos, Pizza, Steak');
3825+
expect(fixture.componentInstance.control.value).toEqual(['tacos-2', 'pizza-1', 'steak-0']);
3826+
}));
3827+
38053828
it('should sort the values that get set via the model based on the panel order',
38063829
fakeAsync(() => {
38073830
trigger.click();
@@ -4182,7 +4205,8 @@ class FloatLabelSelect {
41824205
selector: 'multi-select',
41834206
template: `
41844207
<mat-form-field>
4185-
<mat-select multiple placeholder="Food" [formControl]="control">
4208+
<mat-select multiple placeholder="Food" [formControl]="control"
4209+
[sortPredicate]="sortPredicate">
41864210
<mat-option *ngFor="let food of foods"
41874211
[value]="food.value">{{ food.viewValue }}
41884212
</mat-option>
@@ -4205,6 +4229,7 @@ class MultiSelect {
42054229

42064230
@ViewChild(MatSelect) select: MatSelect;
42074231
@ViewChildren(MatOption) options: QueryList<MatOption>;
4232+
sortPredicate: (a: MatOption, b: MatOption, options: MatOption[]) => number;
42084233
}
42094234

42104235
@Component({

src/lib/select/select.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
381381
}
382382

383383
/**
384-
* A function to compare the option values with the selected values. The first argument
384+
* Function to compare the option values with the selected values. The first argument
385385
* is a value from an option. The second is a value from the selection. A boolean
386386
* should be returned.
387387
*/
@@ -415,9 +415,16 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
415415
/** Input that can be used to specify the `aria-labelledby` attribute. */
416416
@Input('aria-labelledby') ariaLabelledby: string;
417417

418-
/** An object used to control when error messages are shown. */
418+
/** Object used to control when error messages are shown. */
419419
@Input() errorStateMatcher: ErrorStateMatcher;
420420

421+
/**
422+
* Function used to sort the values inside a select in multiple mode.
423+
* Follows the same logic as `Array.prototype.sort`.
424+
*/
425+
@Input() sortPredicate: (a: MatOption, b: MatOption, options: MatOption[]) => number =
426+
(a, b, options) => options.indexOf(a) - options.indexOf(b)
427+
421428
/** Unique id of the element. */
422429
@Input()
423430
get id(): string { return this._id; }
@@ -912,7 +919,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
912919
private _sortValues() {
913920
if (this.multiple) {
914921
const options = this.options.toArray();
915-
this._selectionModel.sort((a, b) => options.indexOf(a) - options.indexOf(b));
922+
this._selectionModel.sort((a, b) => this.sortPredicate(a, b, options));
916923
this.stateChanges.next();
917924
}
918925
}

0 commit comments

Comments
 (0)