Search code examples
angulartypescriptangular-reactive-forms

Unable to set value of select option to first element in ngFor loop with Angular Reactive Forms


I have a select input with an ngFor loop in it to create the options based on colors array.

The behavior i need is to automatically select the first option and to so that angular reactive forms can get its value when the input is created/changed.

So far my form, it looks like this :

<form [formGroup]="stepOneForm">
  <label for="modelSelect">Model:</label>
  <select id="modelSelect" formControlName="model">
    <option value="" selected>Choose...</option>
    <option *ngFor="let model of models" [value]="model.code">
      {{ model.description }}
    </option>
  </select>

  <!-- The input i am interested in -->
  <ng-container *ngIf="stepOneForm.get('model')?.value as code">
    <label for="colorSelect">Color:</label>
    <select id="colorSelect" formControlName="color" (change)="onColorSelected()" [value]="colors[0].code">
      <option *ngFor="let color of colors" [value]="color.code">
        {{ color.description }}
      </option>
    </select>
    <img [src]="imagePath" alt="car">
  </ng-container>
</form>

And my TS part looks like this :

ngOnInit() {
    this.carService.getModels().subscribe(models => {
      this.models = models;
    });

    this.stepOneForm.valueChanges.subscribe(() => {
      this.carService.updateStepOneValidity(this.stepOneForm.valid);
      const model = this.stepOneForm.get('model')?.value
      this.colors = this.models.find(modelObj => modelObj.code === model)?.colors ?? [];
      
      console.log(this.stepOneForm.get('color')?.value) // This returns '' (empty string)
      
      if (model) {
        this.carService.updateShoppingCart(this.stepOneForm.value)
      }
    })
  }

When the ng-container content gets displayed, the subscription gets triggered and on the color input I see a color selected by default, but on this line I get an empty string while i should get the value of this option :

console.log(this.stepOneForm.get('color')?.value)


Solution

  • It's only updating the view, but to update color form control value programmatically when updating model form control, it has to be done with form methods, from the component, with .setValue/.patchValue.

    So, you could add a listener to the first select, and then update color form control.

    <select id="modelSelect" formControlName="model" (change)="onModelSelected()">
    
      onModelSelected() {
        this.stepOneForm.get('color').setValue(this.colors[0]?.code || '');
      }
    

    You could do it from the existing valueChanges by adding {emitEvent: false}, to prevent infinite loop, but then the colors select wouldn't work, so you need to do it from the first select, i.e. you need extra listener.