本文已参与「新人创作礼」活动,一起开启掘金创作之路。
今天遇到了一个新的需求,由于供应商下拉框中供应商的数量过多,滑动选择寻找指定供应商非常困难,于是使用能自动匹配的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赋值给字段即可