Angular 6 Reactive Forms - Set the selected <option> of a <select> FormControl dynamically by condition

Okay, after a couple of hours I got it. Here are my updated .html and .ts files for your interest:

HTML:

<form class="form-container" [formGroup]="customerForm">
    <select formControlName="customer" (change)="onCustomerChanged($event)">
        <option>(new customer)</option>
        <option *ngFor="let customer of customers" [value]="customer.name">
            {{customer.name}}
        </option>
    </select>
    <select formControlName="order">
        <option>(new order)</option>
        <option *ngFor="let order of filteredOrders" [value]="order.id">
            {{order.id}}</option>
    </select>
</form>

.ts-file

export class CustomerFormComponent implements OnInit {

  customerForm: FormGroup;
  selectedCustomer: Customer;
  filteredOrders: Order[];

  customers: Customer[] = [...];
  orders: Order[] = [...];  

  constructor(private route: ActivatedRoute,
              private fb: FormBuilder) { }

  ngOnInit() {
    this.customerForm = this.fb.group({
      customer: "",
      order: ""
    });

    let customerId = Number(this.route.snapshot.paramMap.get('customerId'));
    this.setFormControlValues(customerId);
  }

  setFormControlValues(customerId: number) {
    if (customerId == 0) {
      this.customerForm.get('customer').setValue("(new customer)");
      this.customerForm.get('order').setValue("(new order)");
    }
    else  {
      this.selectedCustomer = this.customers.find(i => i.id == customerId);
      this.filteredOrders = this.getCustomerOrders(this.selectedCustomer);

      this.customerForm.get('customer').setValue(this.selectedCustomer.name);
      this.customerForm.get('order').setValue(this.filteredOrders[0].orderNumber);
    }
  }

  getCustomerOrders(customer: Customer) : Order[] {
    let orders: Order[] = [];

    for (var id = 0; id < customer.orderIds.length; id++)  {
      let orderId = customer.orderIds[id];
      orders.push(this.orders.find(i => i.id == orderId));
    }

    return orders;
  }

  onCustomerChanged(event: any) {
    this.selectedCustomer = this.customers.find(n => n.name == event.target.value);

    this.setFormControlValues(this.selectedCustomer.id);
  }
}

As you can see, in the HTML i now use "[value]" instead of "[ngValue]" and "customer.name" / "order.id" instead of only "customer" / "order".

In the .ts-file I got rid of the "patchValue()" method and brought in the "setValue" method.

Here is the working stackblitz-example:

https://stackblitz.com/edit/angular-conditionaldropdown-gnajge


I think that the problem comes from the object that is selected in the order selector (also in the customers one, in fact).

<option [ngValue]="order" *ngFor="let order of filteredOrders">
    {{order.id}}
</option>

This means that the whole order object will be selected and not only the order id. In this case, if you try to patch the value only with the order.id, then it won't work : the selector wait for an Order object, not the order.id.

I put together a quick stackblitz with a working version of the code you put in the question: https://stackblitz.com/edit/angular-drop-dowb. The only real difference is that

this.customerForm.patchValue({
    orders: this.filteredOrders[0]
});

is used instead of

this.customerForm.patchValue({
    orders: this.filteredOrders[0].id }
});

Update

So, i've updated the stackblitz.

To keep the orders field with the correct values along the customer, you'll need to add a (change) in the select field.

<select formControlName="customers" (change)="updateOrders($event)">
    <option [ngValue]="null">(new customer)</option>
    <option [value]="customer.id" *ngFor="let customer of customers" >
        {{customer.name}}
    </option>
</select>

Don't forget to pass the customer id field as the value instead of the customer - Angular does not seem to like it. With the id value then, you'll need to add a updateOrders function in the component.ts.

  updateOrders(event) {
    const index = this.customers.findIndex(item => item.id == event.target.value);
    if (index === -1) {
      this.customerForm.controls.orders.setValue(null);
      return;
    }
    this.filteredOrders = this.getCustomersOrders(this.customers[index]);
    this.customerForm.controls.orders.setValue(this.filteredOrders[0]);
  }

In the case the id is not in the customers list, then you'll only update the orders with a null value, corresponding to tyour (new order). And if the id is in the customers list, then you'll update the filteredOrders.