Angular 6 Reactive Forms - 根据条件动态设置 <select> FormControl 的选定 <option>

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

我需要知道如何根据条件动态设置 <select> FormControl 的 selected <option>

假设我们有客户下单。有一个下拉菜单,我们 select 一个客户,根据该客户,订单下拉菜单的选项应动态设置,特定订单应 selected。

此外,两个下拉菜单都有一个 <option>“(new customer/order)”,以防还没有客户或订单,所以如果我们在没有客户的情况下转到该表单,这个“新”选项应该在两个下拉菜单中 selected。如果我们有一个没有订单的客户,“新”选项应该在订单下拉菜单中 selected。

这是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>

这是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;
}

目前我正在路由到表单并提供带有 url 的 ID。客户下拉菜单 select 是正确的客户,具体取决于 url.

中的 ID

但是,订单下拉菜单只填充正确,但 selection 不起作用。 selected 根本没有选项。

如果我替换这条语句中的值:

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

"(new order)" 字符串已在 html 中指定:

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

有效。字符串 "(new order)" 在订单下拉菜单中被 select 编辑。它只是不适用于 filteredOrders。

我在这里做错了什么?我可能需要一种完全不同的方法吗?

我认为问题出在订单选择器中选择的对象(实际上也在客户选择器中)。

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

这意味着将选择整个 order 对象,而不仅仅是订单 ID。在这种情况下,如果您尝试仅使用 order.id 修补该值,那么它将不起作用:选择器等待 Order 对象,而不是 order.id

我将一个快速的 stackblitz 与您在问题中输入的代码的工作版本放在一起:https://stackblitz.com/edit/angular-drop-dowb。唯一真正的区别是

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

代替

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

更新

所以,我更新了 stackblitz。

要使 orders 字段沿 customer 保持正确的值,您需要在 select 字段中添加 (change)

<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>

不要忘记传递客户 id 字段作为值而不是 customer - Angular 似乎不喜欢它。使用 id 值,您需要在 component.ts.

中添加一个 updateOrders 函数
  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]);
  }

如果 id 不在客户列表中,那么您只需使用 null 值更新 orders,对应于您的 (new order) .如果 id 在客户列表中,那么您将更新 filteredOrders

好的,几个小时后我明白了。这是我更新的 .html 和 .ts 文件,供您参考:

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-文件

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);
  }
}

如您所见,在 HTML 中,我现在使用“[value]”代替“[ngValue]”,使用“customer.name”/“order.id”代替只有“客户”/“订单”。

在 .ts 文件中,我去掉了“patchValue()”方法并引入了“setValue”方法。

这是有效的 stackblitz 示例:

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

如果您使用的是 [ngValue],则可以在 select 元素上使用 [compareWith],通过比较函数来确定是否选择了该值。使用 [ngValue] 允许复杂对象超过 [value],这是一个字符串。

<form [formGroup]="customerForm">
    <select [compareWith]="compareCustomers" formControlName="customers">
        <option>(new customer)</option>
        <option [ngValue]="customer" *ngFor="let customer of customers">
            {{customer.name}}
        </option>
    </select>
    <select [compareWith]="compareOrders" formControlName="orders">
        <option>(new order)</option>
        <option [ngValue]="order" *ngFor="let order of filteredOrders">
            {{order.id}}
        </option>
    </select>
</form>
export class SomeComponent {
  public compareCustomer(a: Customer, b: Customer): boolean {
    return a.id === b.id;
  }

  public compareOrders(a: Order, b: Order): boolean {
    return a.id === b.id;
  }
}