Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I need to know how I set the selected <option> of a <select> FormControl dynamically by condition.

Let’s say we have customers with orders. There is a dropdown menu where we select a customer and depending on that customer the options for the orders dropdown menu should be set dynamically and a specific order should be selected.

Also both dropdown menus have an <option> “(new customer/order)” in case there is not yet a customer or an order, so if we route to that form without having a customer, this “new” option should be selected in both drop down menus. If we have a customer without orders, the “new” option should be selected in the orders dropdown menu.

This is the form.component.html:

<form [formGroup]="customerForm">
    <select formControlName="customers">
        <option>(new customer)</option>
        <option [ngValue]="customer" *ngFor="let customer of customers">
            {{customer.name}}
        </option>
    </select>
    <select formControlName="orders">
        <option>(new order)</option>
        <option [ngValue]="order" *ngFor="let order of filteredOrders">
            {{order.id}}
        </option>
    </select>
</form>

This is the form.component.ts:

customerForm: FormGroup;

customers: Customer[] = [
{   "id": 1,
    "name": "Meghan Castellano",
    "orderIds": [1,2] },
{   "id": 2,
    "name": "Monika Weiss",
    "orderIds": [3,4] }];

orders: Order[] = [{"id": 1},{"id": 2},{"id": 3},{"id": 4}];

filteredOrders: Order[];

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

ngOnInit() {
    this.customerForm = this.fb.group({
        customers: '',
        orders: '' });

    let customerId = this.route.snapshot.getParam('id');

    if (customerId == 0) {
        this.customerForm.patchValue({
            customers: "(new customer)",
            orders: "(new order)" });
    }
    else
    {
        let customer = this.customers[customerId];

        this.customerForm.patchValue({ customers: customer.name });

        if (customer.orderIds.length != 0) {
            this.filteredOrders = getCustomersOrders(customer);

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

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

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

    return orders;
}

Currently I'm routing to the form and provide an id with the url. The customer dropdown menu selects the correct customer depending on the id in the url.

However, the order dropdown menu is only populated correctly, but the selection does not work. No option at all is selected.

If I replace the value in this statement:

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

with the "(new order)" string already specified in the html:

this.customerForm.patchValue({ order: "(new order)" })

it works. The string "(new order)" is selected in the orders dropdown menu. It just don't work for the filteredOrders.

What am I doing wrong here? Do I maybe need a whole different approach here?

like image 623
PHilgarth Avatar asked Sep 18 '18 13:09

PHilgarth


1 Answers

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

like image 146
PHilgarth Avatar answered Sep 22 '22 01:09

PHilgarth