Angular-Material中Autocomplete调用API的两种用法

1,134 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。
今天遇到了一个新的需求,由于供应商下拉框中供应商的数量过多,滑动选择寻找指定供应商非常困难,于是使用能自动匹配的Input框+下拉框,在中途也遇到了一些小问题故在此记录。

第一种使用场景:作为搜索框进行传值

先放上THML代码

<mat-form-field appearance="outline" class="pr-8" fxFlex="20">
    <mat-label>供应商</mat-label>
    <input type="text" placeholder="" aria-label="" matInput [formControl]="myControl"[matAutocomplete]="auto">
    <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
        <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
            {{option.suppliers_name}}
        </mat-option>
    </mat-autocomplete>
</mat-form-field>
  • mat-autocomplete:自动完成器(input框获得焦点后弹出来的候选下拉框)
  • displayWith:使得表单元素可以展示对应的字符串而非表单本身
  • matAutocomplete:自动完成器的设置,这里只默认使用了auto,其余功能暂未研究

ts代码(有部分省略)

import { map, startWith } from 'rxjs/operators';

export interface Supplier {
  suppliers_id: string;
  suppliers_code: string;
  suppliers_name: string;
}

export class OrderListComponent implements OnInit, AfterViewInit {
    myControl = new FormControl(null);
    filteredOptions: Observable<Supplier[]>;
    suppliersList: Supplier[];
    
    // 更新表格数据
  fetchTableInfo(): void {
    this.isLoading = true;

    let supplierId = null;
    if (this.myControl.value != null) {
      supplierId = this.myControl.value.suppliers_id;
    } else {
      supplierId = null;
    }

    const getParams: { key: string, value: string | number }[] = [
      { key: 'order_status', value: this.searchForm.value.orderStatus },
      { key: 'plan_type', value: this.searchForm.value.planType },
      { key: 'order_id', value: this.searchForm.value.orderId },
      { key: 'products_name', value: this.searchForm.value.productName },
      { key: 'suppliers_id', value: supplierId },
      { key: 'spu', value: this.searchForm.value.spu },
      // { key: 'sku', value: this.searchForm.value.sku },
      { key: 'buyer_id', value: this.searchForm.value.buyerId }
    ];

    const searchParamsStr = this.confSvc.makeGetParamsStr(getParams);
    this.purchaseService.getPurchaseOrderList(this.paginator.pageIndex, searchParamsStr).subscribe(
      res => {
        // console.log(res);
        this.tableData = res['data'].data || [];
        this.tableDataLength = res['data'].count;
      },
      e => console.log(e),
      () => this.isLoading = false
    );

  }
  
  private _filter(name: string): Supplier[] {
    const filterValue = name.toLowerCase();
    return this.suppliersList.filter(option => option.suppliers_name.toLowerCase().includes(filterValue));
  }

  displayFn(supplier: Supplier): string {
    return supplier && supplier.suppliers_name ? supplier.suppliers_name : '';
  }

  // 获取采购供应商列表
  getSuppliersList(): void {
    this.purchaseService.getSuppliersList().subscribe(
      res => {
        this.suppliersList = res['data'] || [];
        this.filteredOptions = this.myControl.valueChanges.pipe(
          startWith(''),
          map(value => (typeof value === 'string' ? value : value.suppliers_name)),
          map(name => (name ? this._filter(name) : this.suppliersList.slice())),
        );
      }
    );
  }
}

suppliers_name:供应商的名称,需要显示在input框中进行显示
suppliers_id:供应商的ID,搜索的时候需要传值调用API

其中有一部分代码是这样的:

let supplierId = null;
    if (this.myControl.value != null) {
      supplierId = this.myControl.value.suppliers_id;
    } else {
      supplierId = null;
    }

这里进行myControl的判定是因为搜索栏为空时,myControl默认为null,而null中无法使用null.value.suppliers_id
当然这里也有另一种不需要判断的解决方法,放在了第二种场景中讲解。

第二种使用场景:需要接受API的值并展示,也需要进行传值

该场景与第一种不同的地方在于,搜索框是使用一个FormGroup进行整合的,每个搜索条件都为FormControl属性。而该页面中由于ngModel更易于双向数据绑定的使用,而所有需要传值以及获取值的字段都使用了ngModel进行绑定。

所以在该页面中只有供应商一个字段需要使用FormControl(以下代码只会展示与第一种使用场景不同的地方)
声明myControl的时候,上述避免判断空值的写法如下:

myControl = new FormControl({
    suppliers_id: '',
    suppliers_code: '',
    suppliers_name: '',
  });

在展示供应商信息的时候也需要对myControl进行赋值:

// 更新表格数据
  fetchTableInfo(): void {
    this.isLoading = true;
    this.purchaseService.getPurchaseOrderDetail(this.orderId).subscribe(
      async res => {
        const DetailInfo = res['data'].basic;
        this.orderDetail = DetailInfo[0];
        this.myControl.setValue({
          suppliers_id: this.orderDetail['suppliers_id'],
          suppliers_code: this.orderDetail['suppliers_code'],
          suppliers_name: this.orderDetail['suppliers_name'],
        });
        this.tableData = res['data'].skuData || [];
      },
      e => console.log(e),
      () => this.isLoading = false
    );
  

注:这里必须使用setValue,在此之前我使用过一种错误的写法:

// 错误写法!
this.myControl.value.suppliers_id = this.orderDetail['suppliers_id'];
this.myControl.value.suppliers_name = this.orderDetail['suppliers_name'];
this.myControl.value.suppliers_code = this.orderDetail['suppliers_code'];

这样的赋值方式是无法生效的,无论放在生命周期的哪个位置(我尝试过了)

需要传值的时候也很简单

// 保存
  async saveOrder(): Promise<void> {
    this.isLoading = true;
    const res = await this.purchaseService.saveOrderDetail({
      order_id: this.orderDetail['order_id'],
      suppliers_id: this.myControl.value.suppliers_id,
      buyer_id: this.orderDetail['buyer_id'],
      note: this.orderDetail['note'],
      skuData: this.tableData
    });
    this.confSvc.handleTip(res['msg']);
    this.fetchTableInfo();
    this.isLoading = false;
  }

直接将this.myControl.value.suppliers_id赋值给字段即可