Skip to content

Commit 50ec808

Browse files
fix(cdk/collections): SelectionModel does not always respect the compareWith function (#26447)
Fixed bug in SelectionModel where compareWith function was not consistently respected in deselect method. Fixes #25878
1 parent c4414ad commit 50ec808

File tree

2 files changed

+37
-9
lines changed

2 files changed

+37
-9
lines changed

src/cdk/collections/selection-model.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,7 @@ export class SelectionModel<T> {
131131
* Determines whether a value is selected.
132132
*/
133133
isSelected(value: T): boolean {
134-
if (this.compareWith) {
135-
for (const otherValue of this._selection) {
136-
if (this.compareWith(otherValue, value)) {
137-
return true;
138-
}
139-
}
140-
return false;
141-
}
142-
return this._selection.has(value);
134+
return this._selection.has(this._getConcreteValue(value));
143135
}
144136

145137
/**
@@ -191,6 +183,7 @@ export class SelectionModel<T> {
191183

192184
/** Selects a value. */
193185
private _markSelected(value: T) {
186+
value = this._getConcreteValue(value);
194187
if (!this.isSelected(value)) {
195188
if (!this._multiple) {
196189
this._unmarkAll();
@@ -208,6 +201,7 @@ export class SelectionModel<T> {
208201

209202
/** Deselects a value. */
210203
private _unmarkSelected(value: T) {
204+
value = this._getConcreteValue(value);
211205
if (this.isSelected(value)) {
212206
this._selection.delete(value);
213207

@@ -238,6 +232,20 @@ export class SelectionModel<T> {
238232
private _hasQueuedChanges() {
239233
return !!(this._deselectedToEmit.length || this._selectedToEmit.length);
240234
}
235+
236+
/** Returns a value that is comparable to inputValue by applying compareWith function, returns the same inputValue otherwise. */
237+
private _getConcreteValue(inputValue: T): T {
238+
if (!this.compareWith) {
239+
return inputValue;
240+
} else {
241+
for (let selectedValue of this._selection) {
242+
if (this.compareWith!(inputValue, selectedValue)) {
243+
return selectedValue;
244+
}
245+
}
246+
return inputValue;
247+
}
248+
}
241249
}
242250

243251
/**

src/cdk/collections/selection.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,4 +279,24 @@ describe('SelectionModel', () => {
279279
let singleSelectionModel = new SelectionModel();
280280
expect(singleSelectionModel.isMultipleSelection()).toBe(false);
281281
});
282+
283+
it('should deselect value if comparable to another one', () => {
284+
type Item = {key: number; value: string};
285+
const v1: Item = {key: 1, value: 'blue'};
286+
const v2: Item = {key: 1, value: 'green'};
287+
const compareFun = (x: Item, y: Item) => x.key === y.key;
288+
const model = new SelectionModel<Item>(false, [v1], false, compareFun);
289+
model.deselect(v2);
290+
expect(model.selected.length).toBe(0);
291+
});
292+
293+
it('should not deselect value if not comparable to another one', () => {
294+
type Item = {key: number; value: string};
295+
const v1: Item = {key: 1, value: 'blue'};
296+
const v2: Item = {key: 2, value: 'apple'};
297+
const compareFun = (x: Item, y: Item) => x.key === y.key;
298+
const model = new SelectionModel<Item>(false, [v1], false, compareFun);
299+
model.deselect(v2);
300+
expect(model.selected.length).toBe(1);
301+
});
282302
});

0 commit comments

Comments
 (0)