web-dev-qa-db-ja.com

Angular 6リアクティブフォーム-<select> FormControlの選択された<option>を条件によって動的に設定します

選択した_<option>_ FormControlの_<select>_を条件によって動的に設定する方法を知る必要があります。

注文した顧客がいるとします。顧客を選択するドロップダウンメニューがあり、その顧客に応じて注文ドロップダウンメニューのオプションを動的に設定し、特定の注文を選択する必要があります。

また、両方のドロップダウンメニューには_<option>_「(新しい顧客/注文)」があり、顧客または注文がまだない場合、顧客なしでそのフォームにルーティングする場合、この「新しい」オプションは両方のドロップダウンメニューで選択されています。注文のないお客様がいる場合は、注文ドロップダウンメニューで「新しい」オプションを選択する必要があります。

これは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を提供しています。顧客のドロップダウンメニューは、URLのIDに応じて正しい顧客を選択します。

ただし、注文ドロップダウンメニューは正しく設定されているだけで、選択は機能しません。オプションがまったく選択されていません。

このステートメントの値を置き換える場合:

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

"(new order)"文字列がHTMLで既に指定されている場合:

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

できます。文字列"(new order)"が注文ドロップダウンメニューで選択されています。 FilteredOrdersでは機能しません。

ここで何が悪いのですか?ここでまったく異なるアプローチが必要ですか?

4
PHilgarth

さて、数時間後に私はそれを得ました。ここにあなたの興味のために私の更新された.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では「[ngValue]」の代わりに「[value]」を使用し、「customer」/「order」だけではなく「customer.name」/「order.id」を使用しています。

.tsファイルで、「patchValue()」メソッドを削除し、「setValue」メソッドを導入しました。

これが実際のstackblitzの例です:

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

6
PHilgarth

問題は、注文セレクターで選択されたオブジェクト(実際には顧客のものでもある)に起因すると思います。

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

つまり、注文IDだけでなく、orderオブジェクト全体が選択されます。この場合、order.idのみを使用して値にパッチを適用しようとしても、機能しません。セレクタは、order.idではなくOrderオブジェクトを待機します。

私はあなたが質問に入れたコードの作業バージョンと一緒に簡単な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 valueの場合、component.tsupdateOrders関数を追加する必要があります。

  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が顧客リストにない場合、orders(new order)に対応するnull値でのみ更新します。また、idが顧客リストにある場合は、filteredOrdersを更新します。

1
RoadEx