Search code examples
angularngforangular-directiveangular-ng-ifmat-select

Angular Select not showing text when the combination of *ngFor and *ngIf is used


I have a form with 4 select inputs. When one value is selected it should not be shown in other select inputs. I have created a working solution. But when an option is selected it is not shown in the select text, it stays empty. it occurs when a *ngFor directive is used along with an *ngIf directive. As alternative solution I removed the if directive and added a [hidden] with a function that does the same check.

Code that is working properly:


<div>
    <mat-form-field class="full-width">
      <mat-label>Player 1</mat-label>
      <mat-select [(ngModel)]="selectedPlayers[1]">
            <mat-option [value]="player" [hidden]="isSelected(player)" *ngFor="let player of players">
              {{ player.name }}
            </mat-option>
      </mat-select>
    </mat-form-field>
  <div>
    <mat-form-field class="full-width">
      <mat-label>Player 2</mat-label>
      <mat-select [(ngModel)]="selectedPlayers[2]">
          <mat-option [value]="player" [hidden]="isSelected(player)" *ngFor="let player of players">
            {{ player.name }}
          </mat-option>
      </mat-select>
    </mat-form-field>
  </div>
  <div>
    <mat-form-field class="full-width">
      <mat-label>Player 3</mat-label>
      <mat-select [(ngModel)]="selectedPlayers[3]" >
          <mat-option [value]="player" [hidden]="isSelected(player)" *ngFor="let player of players">
            {{ player.name }}
          </mat-option>
      </mat-select>
    </mat-form-field>
  </div>
  <div>
    <mat-form-field class="full-width">
      <mat-label>Player 4</mat-label>
      <mat-select [(ngModel)]="selectedPlayers[4]" >
          <mat-option [value]="player" [hidden]="isSelected(player)" *ngFor="let player of players">
            {{ player.name }}
          </mat-option>
      </mat-select>
    </mat-form-field>
  </div>
  
  <div mat-dialog-actions>
    <button mat-raised-button mat-dialog-close>No Thanks</button>
    <button mat-raised-button mat-dialog-close>Start</button>
  </div>

Code that works, but doesn't show the selected option text in the select (same structure as above, but only select part as example):

<mat-select [(ngModel)]="selectedPlayers[3]">
   <ng-container *ngFor="let player of players">
      <mat-option [value]="player" *ngIf="!selectedPlayers.includes(player)">
            {{ player.name }}
      </mat-option>
   </ng-container>
</mat-select>

I want to know why the combination of the ngFor and ngIf is preventing the select text being set when an option is selected?

Ps. I'm new to Angular and could not find a clear answer behind this process.

Edit:

Below is the used .ts file:

import { Component } from '@angular/core';

export interface Player {
  name: string;
}

@Component({
  selector: 'app-new-game-dialog',
  templateUrl: './new-game-dialog.component.html',
  styleUrls: ['./new-game-dialog.component.css'],
})
export class NewGameDialogComponent {
  players: Player[] = [
    { name: 'Brian'},
    { name: 'Clement'},
    { name: 'Jordy'},
    { name: 'Mitchel'},
    { name: 'Nicky'},
  ];
  selectedPlayers: Player[] = [];

  isSelected(player: Player) {
    return this.selectedPlayers.includes(player)
  }

  
}

Visual example:

hidden with function show select text

Here you can see a dialog that has four select inputs. On the first one "brian" is selected. In the third select brian is not available anymore. This is working correctly. But if I use the second snippet with the *ngIf directive instead of the [hidden] function, the text 'brian' is not visible in the select input.

below is a screenshot where I selected brian in the first select. It is not available in the third select, but the text 'brian' itself is not visible in the first select

ngIf used not showing select text


Solution

  • It comes down to the difference between what *ngIf and hidden do. When *ngIf is false, it actually removes the element from the DOM. hidden just makes it invisible in the UI. What is the difference between *ngIf and [hidden]?

    mat-select looks for a mat-option element with a value equal to its [(ngModel)] value. *ngIf is removing that option, so there will never be an option that matches.