如何阻止材料下拉列表自动完成选择在 Angular 8/9 中触发另一个搜索查询?



摘要:我有一个按您键入的工作字段下拉搜索。当我从下拉列表中选择时,它愉快地将字段设置为我在搜索中收到的对象,但不高兴地注册该更改并将整个对象发送出去进行搜索。

.HTML:

<form [formGroup]="myForm" novalidate>
<mat-form-field>
<input matInput 
placeholder="SKU / Item Number" 
[matAutocomplete]="auto" 
formControlName='itemName'>
</mat-form-field> 
<mat-autocomplete #auto="matAutocomplete" [displayWith]="parseDropDownSelection">
<mat-option 
*ngFor="let row of searchQueryResult" 
[value]="row" 
(onSelectionChange)="onItemSelection($event)">
<span>{{ row.Name }}</span>
</mat-option>
</mat-autocomplete>   
</form>

设置:

import {FieldSearchServiceItem}     from './../services/field-search.service';
constructor(
private dialog: MatDialog,
private formBuilder: FormBuilder,
private http: HttpClient,
private appServiceItem: FieldSearchServiceItem,    
)   {}

ngOnInit((

ngOnInit(){
this.myForm = this.formBuilder.group
({
itemName: '',
});
this.myForm
.get('itemName')
.valueChanges
.pipe(
debounceTime(200),
switchMap(value => this.appServiceItem.search({name: value}, 1))
)
.subscribe(serviceResult => this.searchQueryResult = serviceResult.qResult);
}

服务内容:

@Injectable()
export class FieldSearchServiceItem 
{
constructor(private http: HttpClient) {}
search(filter: {name: string} = {name: ''}, page = 1): Observable<apiQueryResponseForItem> 
{
var queryResponse;
return this.http.get<apiQueryResponseForItem>(`example.com/search/item/${filter.name}`)
.pipe(
tap((response: apiQueryResponseForItem) => 
{
response.qResult = response.qResult
.map(unit => new ItemUnit(unit.Id, unit.Name))
return response;
})
);
}
}

类定义:

export class ItemUnit
{
constructor
(
public Id:number,
public Name:string,
)   {}
}
export interface apiQueryResponseForItem
{
qSuccess: boolean;
qResult: ItemUnit[];        
}

我见过其他答案,其中的解决方案是在设置值时使用 emitEvent:false,如下所示:

this.myForm.get('itemName').patchValue('theDataThatCameBackFromSeach', {emitEvent:false})

这是有道理的...但我感觉解决方案与这种可观察/可注射/材料方法不匹配......主要是因为我没有使用执行 .setValue(( 或 .patchValue(( 的调用,我猜在 Material 东西中的某个地方有一个绑定来处理它。

服务器最终看到如下调用:

http://example.com/search/item/a                   (when the letter a is typed)
http://example.com/search/item/[object%20Object]   (after clicking the dropdown, JS tries to search 'object' after clumsily falling back to string representation )

我的onItemSelection()目前不参与,它正在工作,但除了转储到控制台之外什么都不做。从我的searchQueryResult服务返回的.qResult包含{Id:number,Name:string}。如何让自动完成的字段设置操作仍然执行其设置字段的工作,但在执行此操作时创建更改事件,同时仍遵守onItemSelection()以便我可以完成其他处理?

我前段时间遇到了同样的问题,这是我在系统中用于多个输入的最终解决方案。

步骤如下:

  1. 意识到这里实际上有两个事件。键入的内容和选择的值
  2. 为这两个事件创建两个可观察量并分别处理它们。
class Component {
ngOnInit() {
/*       SEPARATE THE EVENTS      */
// If it's an object, it's been selected.
// I also don't allow selecting `null` but it's up to you.
const itemSelected$ = this.myForm.get('itemName').valueChanges.pipe(
filter(val => typeof val === 'object' && val !== null),
);
// If it's a string, it's been typed into the input
const itemTyped$ = this.myForm.get('itemName').valueChanges.pipe(
filter(val => typeof val === 'string'),
);
/*       HANDLE ITEM SELECTED     */
itemSelected$.subscribe(item => {
// If you want, you can also handle "fake" items here.
// I use this trick to show a placeholder like "Create New Item" in the dropdown
if (item.id === 0 && item.name === 'Create New Item') {
this.createNewItem().subscribe(
newItem => this.myForm.get('itemName').setValue(newItem),
);
return;
}
// I use this in a custom input component (with ControlValueAccessor)
// So this is where I notify the parent
this.onChange(item);
});
/*       HANDLE ITEM TYPED        */
const searchQueryResult$ = itemTyped$.pipe(
debounce(200),
tap(value => {/* you could handle starting a loading spinner or smth here */}),
switchMap(name => this.appServiceItem.search({name}, 1)),
);
// now you can either use searchQueryResult$ with async pipe:
// this.filteredResults$ = searchQueryResult$;
// or subscribe to it and update a field in your component class:
searchQueryResult$.subscribe(result => this.searchQueryResult = result);
// If you subscribe, don't forget to clean up subscriptions onDestroy
}
}

我冒昧地添加了一些建议和技巧,但您大致了解了 - 创建两个单独的互斥可观察量并分别处理它们。

在Stackblitz中做了一个简单的应用程序,你可以看看它。希望我正确理解了您的问题。

主要思想是 - 当用户填写表单输入时,我们得到字符串,当自动完成填写它时,我们得到 ItemUnit。 这样我们就可以决定是否发出 http 请求。

假设这是您的界面

interface MyClass {
id: number,
name: string
}

内部值更改有一个检查

if(!(value instanceof MyClass)) { //callApi }

最新更新