Angular: 2-way binding with mat-select not working, assigning value in code causes it to reset in UI

Viewed 1316

I'm trying to bind a mat-select to an enum value like this:

<mat-form-field>
  <mat-label>Type</mat-label>
  <mat-select [(ngModel)]="selectedType" [compareWith]="compareType">
    <mat-select-trigger>
      {{types[selectedType]?.name}}
    </mat-select-trigger>
    <mat-option *ngFor="let type of types | keyvalue" [value]="type.key">
      <mat-icon>{{type.value.icon}}</mat-icon> {{type.value.name}}
    </mat-option>
  </mat-select>
</mat-form-field>

And the typescript:

enum Type {
  Hardware = 0,
  Software = 1
}

types: { [TP in number]: { icon: string, name: string }; } = {
  [Type.Hardware]: { icon: 'computer', name: 'Hardware' },
  [Type.Software]: { icon: 'keyboard', name: 'Software' },
};

selectedType: Type;

compareType(type1: Type, type2: Type): boolean {
  return ((type1 as Type) === (type2 as Type));
}

I want the value of the select to be bound to selectedType, which I'm trying to achieve using [(ngModel)]="selectedType" [compareWith]="compareType".

The problem is, that this data-binding doens't fully go both ways. When I assign a value using the UI, the binding updates accordingly, but when I update the value inside my code, it resets the value in the UI, showing the placeholder again. However while the UI doesn't display it correctly, the value is assigned properly.

1 Answers

there is type mismatch problem in your code.

selectedType has type Type which is a number.

where you set[value]="type.key" within

<mat-option *ngFor="let type of types | keyvalue" [value]="type.key">

type.key is a string due to keyvalue pipe.

in the code above when you a select an item from dropdown as string value of "0" or "1" is assigned to selectedType. if you assign a value programatically like this.selectedType = Type.Hardware a number value 0 is assigned to selectedType

and since your compareType function uses strict equality (===) it returns false because 1 === "1" is false

you can either;

  • make your enum Type have string values;
enum Type {
  Hardware = "0",
  Software = "1"
}
  • or, convert type.key to number while binding to mat-option
[value]="+type.key"
  • or, use equality (==) instead of strict equality (===) in compareType function. (i personally do not recommend this one!)
  compareType(type1: Type, type2: Type): boolean {
    return ((type1 as Type) == (type2 as Type));
  }

here is a working demo

Related