Angular2/4 mat-select multiple ngModel

Maiur Laximidas picture Maiur Laximidas · Oct 10, 2017 · Viewed 21.5k times · Source

I have a mat-select dropdownlist with multiple enabled and am using NgModel to store user selected values.

Problem is when I navigate to another page and come back the user-selected values aren't in the mat-select..I know the ngModel has those values...I am missing something...

HTML

<mat-form-field>
 <mat-select placeholder="Customers" name="customerDetails" ngDefaultControl       
 formControlName="customerDetails" [(ngModel)]="custonerDetails" 
 [formControl]="customerDetailsCtrl" multiple   
 (ngModelChange)="onCustomerValueChanges(customer)" >
 
  <mat-option *ngFor="let customer of customerDetailsResult"
  [value]="customer">{{customer.CustomerNo}}- 
                     {{customer.CustomerDescription}}
   </mat-option>
 </mat-select>
</mat-form-field>

Any ideas?

Answer

RedDree picture RedDree · Apr 6, 2018

Depending on a use-case initializing some default options as selected might not work by simply binding to the ngModel, because objects in the options and in the selected subset from the previous state have different identities. Thanks to the support for compareWith it is possible to set them as selected.

Have a look at the offical Angular docs here.

In Material2 demo-app they have an example of the function with two implementations. It's here.

In my component I have a collection of User objects [people] for the options of mat select. The component receives a collection of selected User objects [users] as Input from previous state. Fair enough, objects in [people] and objects in [users] have different identities and the subset in the multiple select does not initialize with selected checkboxes by default.

So, the magical compareWith just literally compares objects by some given values and returns true or false, and the checkboxes on the subset of [people] get the status of selected. In my code I decided to go with [(ngModel]) binding:

<mat-form-field>
    <mat-select [compareWith]="compareFn" name="users" [(ngModel)]="users" multiple>
        <mat-option *ngFor="let person of people" [value]="person">
           {{ person.username }}
        </mat-option>
   </mat-select>
</mat-form-field>

And in the .ts file I utilize the function from the Angular doc to return true if two User objects have the same id:

compareFn(user1: User, user2: User) {
    return user1 && user2 ? user1.id === user2.id : user1 === user2;
}

If you have a similar use-case, it might work out-of-the-box.

On the what's-under-the-hood note, compareWith made me curious. I found out that it is based on a function in Angular2 called looseIdentical (have a look here), which in turn derives from the identical in Dart.js library by Google. It can be found here.