How does angular's selection model determine equality of two objects?

Issue

I’m trying to select pre-select some users in a table using Angular’s selectionmodel. The call retrieving the users in the table and the call retrieving the already selected users are different so the actual objects are not the same.

I tried writing an equals method on the UserProfile class, this does not seem to change anything. Rewriting the code to use id’s would fix the problem but i would like to have the selection model handling the actual objects instead of id’s.

This is the code i’m using, but i hope my question is clear enough.

@Input() selected: UserProfile[];

ngOnInit() {
    this.selection = new SelectionModel<UserProfile>(true, this.selected);

Solution

The SelectionModel is implemented as a part of @angular/cdk library. The documentation can be found in the Collections page from Angular Material Documentation.

In the code we use the following import:

import { SelectionModel } from '@angular/cdk/collections';

The SelectionModel is build using the native JavascriptSet() object, as can be found in the source code:

private _selection = new Set<T>();

The Set object lets you store unique values of any type, whether primitive values or object references.

What we need to consider first is that, in Javascript, two different instances of objects are always not equal:

const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2 };
obj1 !== obj2; // => true

So, the following situation will arive:

const mySet1 = new Set();

const obj1 = {a: 1, b: 2};
mySet1.add(obj1);

const obj2 = {a: 1, b: 2};
mySet1.add(obj2)   // obj2 is referencing a different object, so the object will be added in the set

More info about JS Set() here.

Now, what we really need is a deep value equality check between the objects in our set. Unfortunatelly, there is no method to override the comparison method in the Set object, so I’ve wrote my own implementation of the SelectionModelSelectionModelImmutableJS by using the very popular library called immutable-js. The implementation was inspired by the following answer.

To simplify, by using immutable-js, we’ll have the following situation:

const { Map, Set } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = Map({ a: 1, b: 2, c: 3 });
map1 !== map2; // => true, two different instances are not reference-equal
map1.equals(map2); // true, but are value-equal if they have the same values
const set = Set().add(map1);
set.has(map2); // true, because these are value-equal

The code for the Selection Model is a little bit too large and I will not post it inline – it can be found in the working demo.

In the app we’ll use:

import { SelectionModelImmutableJS } from './selection-model-immutable';

.....


public selection = new SelectionModelImmutableJS<IPeriodicElement>(
    true,
    this.initialSelection
  );

The full working demo:
https://stackblitz.com/edit/angular-ivy-wnvohl

Answered By – andreivictor

Answer Checked By – Gilberto Lyons (AngularFixing Admin)

Leave a Reply

Your email address will not be published.