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
.