Home > Software engineering >  ngFor and [value] in a <select> in Angular 13
ngFor and [value] in a <select> in Angular 13

Time:09-09

I've found something that looks like a bug but maybe I'm just missing something. When I use '[value]=nrSelect' it works so long as I am not using ngFor to populate the <select>. For example I put 2 dropdowns, one with ngFor and one without:

<select [value]='nrSelect' class='form-control'>
  <option value='A'>A</option>
  <option value='B'>B</option>
  <option value='C'>C</option>
</select>
<br/>
<select [value]='nrSelect' class='form-control'>
  <option *ngFor="let option of options" [value]='option.value'>{{option.label}}</option>
</select>

and in the .ts file I select option "B":

export class AppComponent {
  title = 'angular-tour-of-heroes';
  options = [
    { label: "A", value: "A" },
    { label: "B", value: "B" },
    { label: "C", value: "C" },
  ];
  nrSelect = "B";
}

This produces the following when loaded:

enter image description here

I would have expected both to select option "B" but only the one without ngFor works. My angular version is 13.1.4. Both dropdowns work if I use [(ngModel)]=nrSelect but I'm interested in knowing why [value]=nrSelect doesn't work.

Why is that - is it a real bug or am I just not doing it correctly?

CodePudding user response:

This is really interesting.
I think it's by design as now they are respecting the standard.
Here's the thing: the select tag doesn't have a value attribute!

My guess is that since value is not part of the HTML <select> spec. and the option list was dynamically created there's no way to keep track of the items to pre-select the "B" option. Meaning that you will have to introduce either Reactive forms (via FormBuilder) or Template-driven forms (via ngModel) to keep track of the value. One point that supports this theory is that if you make sure that the B option is there from the very beginning it will be pre-selected.

One way to make it work out-the-box is binding to the [selected] property of the <option> instead.

<select >
  <option
    *ngFor="let option of options"
    [value]="option.value"
    [selected]="option.value === nrSelect"
  >
    {{ option.label }}
  </option>
</select>

Now my thumb rule would be: We can bind to the control's value property as long as the value is backed by the HTML spec. otherwise we need to introduce Angular Forms (either Reactive or Template Driven).

Oddly enough, it works with Angular 12 but not with Angular 13 or 14 :)

  • Related