You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular动态添加输入框的Autocomplete搜索筛选失效求助

问题分析与解决方案

核心问题原因

  1. 共享Observable实例:所有自动完成组件绑定到同一个filteroptions Observable,每次添加新字段时该Observable会被覆盖,仅最后一个字段的控件能驱动它。
  2. 冲突的表单控件绑定:输入框同时使用formControlName和[formControl]属性,Angular无法同时处理这两种绑定,导致控件状态异常。

分步修复

1. 重构组件代码,为每个字段创建独立的Observable

  • 替换单个filteroptions变量为数组,存储每个产品字段对应的自动完成筛选Observable。
  • 移除无用的formcontrol变量。
  • 添加产品时将新的Observable推入数组,删除产品时同步移除对应项。

修改后的TypeScript关键代码:

// 替换原filteroptions变量
filterOptionsList: Observable<string[]>[] = [];
// 移除formcontrol = new FormControl('');

addNewProduct() {
  this.items = this.productFormarray.get('productdetails') as FormArray;
  const newProduct = this.createNewProduct();
  this.items.push(newProduct);
  const indexvalue = this.items.length - 1;
  this.productFormarray.get('productdetails').controls[indexvalue].get('quantit').setValue(this.quantitylist[1]);
  
  const productNameControl = newProduct.get('productname');
  if (productNameControl) {
    // 创建当前字段的筛选Observable并推入数组
    const filterOptions = productNameControl.valueChanges.pipe(
      startWith(''),
      map(value => this._filter(value))
    );
    this.filterOptionsList.push(filterOptions);
    
    productNameControl.valueChanges.subscribe(selectedProductName => {
      this.onProductChange(selectedProductName, indexvalue);
    });
  }
  
  // 其余quantity和price的订阅代码保持不变
}

removeProduct(index: any) {
  this.items = this.productFormarray.get('productdetails') as FormArray;
  this.items.removeAt(index);
  // 同步移除对应位置的筛选Observable
  this.filterOptionsList.splice(index, 1);
}

2. 修复HTML中的绑定问题

  • 移除输入框上的[formControl]="formcontrol"属性,保留formControlName="productname"即可。
  • 将自动完成的数据源改为对应索引的Observable。

修改后的HTML关键部分:

<tr *ngFor="let product of productdetailsarray.controls; let i=index" [formGroupName]="i">
  <td>
    <div class="formGroup">
      <input formControlName="productname" matInput type="text" [matAutocomplete]="auto" class="form-control" />
      <mat-autocomplete #auto="matAutocomplete">
        <mat-option *ngFor="let product of filterOptionsList[i] | async" [value]="product">
          {{product}}
        </mat-option>
      </mat-autocomplete>
    </div>
  </td>
  <!-- 其余单元格代码保持不变 -->
</tr>

完整修正代码

HTML代码

<form [formGroup]="productFormarray" (ngSubmit)="onSubmit()">
<div class="reg-right">
    <div class="formGroup">
        <label for="customername" class="form-label">Customer Name</label>
        <input type="text" class="form-control" id="customername" placeholder="Customer Name"
            formControlName="customername">
    </div>
    <div class="formGroup" formArrayName="productdetails">
        <div class="table-responsive">
            <table class="table table-bordered" style="margin-top: 20px;">
                <thead>
                    <tr>
                        <td style="width:40%">
                            Product Name
                        </td>
                        <td style="width:15%">
                            Quantity
                        </td>
                        <td style="width:15%">
                            Price
                        </td>
                        <td style="width:15%">
                            Gst
                        </td>
                        <td>
                        </td>
                    </tr>
                </thead>
                <tr *ngFor="let product of productdetailsarray.controls; let i=index" [formGroupName]="i">
                    <td>
                        <div class="formGroup">
                            <input formControlName="productname" matInput type="text" [matAutocomplete]="auto"
                                class="form-control" />
                            <mat-autocomplete #auto="matAutocomplete">
                                <mat-option *ngFor="let product of filterOptionsList[i] | async" [value]="product">
                                    {{product}}
                                </mat-option>
                            </mat-autocomplete>
                        </div>
                    </td>
                    <td>
                        <div class="formGroup">
                            <select class="form-control" id="quantit" formControlName="quantit" name="quantit">
                                <option *ngFor="let quantity of quantitylist" [ngValue]="quantity">
                                    {{quantity}}
                                </option>
                            </select>
                        </div>
                    </td>
                    <td>
                        <div class="formGroup">
                            <input type="text" class="form-control" id="price" formControlName="price"
                                placeholder="Price " readonly name="price">
                        </div>
                    </td>
                    <td>
                        <div class="formGroup">
                            <input type="text" class="form-control" id="gst" formControlName="gst" placeholder="Gst"
                                name="gst" readonly>
                        </div>
                    </td>
                    <td>
                        <a type="button" class="form-control btn btn-primary" style="background-color: red;"
                            (click)="removeProduct(i)">Remove (-)</a>
                    </td>
                </tr>
            </table>
        </div>
        <a type="button" class="btn btn-secondary" style="background-color: green;"
            (click)="addNewProduct()">Add(+)</a>
        <br />
    </div>
    <br />
    <br />
    <div class="row">
        <div class="col-md-6">
        </div>
        <div class="col-md-6">
            <div class="formGroup">
                <label for="totalprice" class="form-label" style="margin-top: 10pt;">Total Product Price</label>
                <input type="text" class="form-control form-control1" id="totalprice" formControlName="totalprice"
                    placeholder="totalprice" name="totalprice" style="margin-left: 20pt; float:right" readonly>
            </div>
        </div>
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>

TypeScript代码

import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, FormControl, FormArray, FormBuilder, Validators } from '@angular/forms'
import { Observable, map, startWith, Subscription } from 'rxjs';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit, OnDestroy {
  productFormarray: any;
  quantitylist = [0.5, 1, 1.5];
  items!: FormArray;
  totalGstPrice: number = 0;
  totalProductPrice: number = 0;
  productlist = [{ productname: "apple", price: 10, gst: 10 }, { productname: "orange", price: 20, gst: 12 }, { productname: "lemon", price: 30, gst: 20 }];
  productlistss = ['apple', 'lemon', 'orange'];
  filterOptionsList: Observable<string[]>[] = [];
  // 存储订阅实例以防止内存泄漏
  private subscriptions: Subscription[] = [];

  constructor(private fb: FormBuilder) {
    this.productFormarray = new FormGroup({
      customername: new FormControl('', Validators.required),
      productdetails: new FormArray([]),
      remember: new FormControl('true'),
      totalprice: new FormControl(''),
    })
  }

  private _filter(value: string): string[] {
    const searchvalue = value.toLocaleLowerCase();
    return this.productlistss.filter(option => option.toLocaleLowerCase().includes(searchvalue));
  }

  onProductChange(selectedProductName: string, index: number) {
    const selectedProduct = this.productlist.find(
      (product) => product.productname === selectedProductName
    );
    if (selectedProduct) {
      const productDetailsArray = this.productFormarray.get(
        'productdetails'
      ) as FormArray;
      if (productDetailsArray && productDetailsArray.at(index)) {
        const quantityControl = productDetailsArray.at(index).get('quantit');
        if (quantityControl) {
          const quantity = quantityControl.value;
          const price = selectedProduct.price * quantity;
          const priceControl = productDetailsArray.at(index).get('price');
          if (priceControl) {
            priceControl.setValue(price);
          }
        }
      }
    }
  }

  onQuantityChange(selectedQuantity: number, index: number) {
    const productDetailsArray = this.productFormarray.get(
      'productdetails'
    ) as FormArray;
    if (productDetailsArray && productDetailsArray.at(index)) {
      const productNameControl = productDetailsArray.at(index).get('productname');
      if (productNameControl) {
        const selectedProductName = productNameControl.value;
        const selectedProduct = this.productlist.find(
          (product) => product.productname === selectedProductName
        );
        if (selectedProduct) {
          const price = selectedProduct.price * selectedQuantity;
          const priceControl = productDetailsArray.at(index).get('price');
          if (priceControl) {
            priceControl.setValue(price);
          }
        }
      }
    }
  }

  onPriceChange(selectedQuantity: number, index: number) {
    const productDetailsArray = this.productFormarray.get('productdetails') as FormArray;
    if (productDetailsArray && productDetailsArray.at(index)) {
      const productNameControl = productDetailsArray.at(index).get('productname');
      if (productNameControl) {
        const selectedProductName = productNameControl.value;
        const selectedProduct = this.productlist.find(
          (product) => product.productname === selectedProductName
        );
        if (selectedProduct) {
          const priceControl = productDetailsArray.at(index).get('price');
          const gst = ((selectedProduct.gst * priceControl?.value) / 100);
          const gstControl = productDetailsArray.at(index).get('gst');
          gstControl?.setValue(gst);
        }
      }
    }
  }

  addNewProduct() {
    this.items = this.productFormarray.get('productdetails') as FormArray;
    const newProduct = this.createNewProduct();
    this.items.push(newProduct);
    const indexvalue = this.items.length - 1;
    this.productFormarray.get('productdetails').controls[indexvalue].get('quantit').setValue(this.quantitylist[1]);
    
    const productNameControl = newProduct.get('productname');
    if (productNameControl) {
      const filterOptions = productNameControl.valueChanges.pipe(
        startWith(''),
        map(value => this._filter(value))
      );
      this.filterOptionsList.push(filterOptions);
      
      const sub = productNameControl.valueChanges.subscribe(selectedProductName => {
        this.onProductChange(selectedProductName, indexvalue);
      });
      this.subscriptions.push(sub);
    }
    
    const quantityControl = newProduct.get('quantit');
    if (quantityControl) {
      const sub = quantityControl.valueChanges.subscribe(selectedQuantity => {
        this.onQuantityChange(selectedQuantity, indexvalue);
      });
      this.subscriptions.push(sub);
    }
    
    const priceControl = newProduct.get('price');
    if (priceControl) {
      const sub = priceControl.valueChanges.subscribe((selectedProductName) =>
        this.onPriceChange(selectedProductName, indexvalue)
      );
      this.subscriptions.push(sub);
    }
  }

  createNewProduct(): FormGroup {
    return new FormGroup({
      productname: new FormControl('', Validators.required),
      quantit: new FormControl('1', Validators.required),
      price: new FormControl('', Validators.required),
      gst: new FormControl('', Validators.required)
    })
  }

  removeProduct(index: any) {
    this.items = this.productFormarray.get('productdetails') as FormArray;
    this.items.removeAt(index);
    this.filterOptionsList.splice(index, 1);
  }
  
  get productdetailsarray() {
    return this.productFormarray.get('productdetails') as FormArray;
  }

  ngOnInit() {
    this.addNewProduct();
  }
  
  ngOnDestroy() {
    // 清理所有订阅防止内存泄漏
    this.subscriptions.forEach(sub => sub.unsubscribe());
  }
  
  onSubmit() {
    console.log(this.productFormarray.value);
  }
}

额外优化说明

  • 添加了OnDestroy接口和订阅管理,防止内存泄漏。
  • 移除了HTML中重复的class="formGroup"属性。
  • 保留了原有的业务逻辑,仅修复自动完成功能的问题。

内容的提问来源于stack exchange,提问作者Reegan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 22:34:50