摘要:我有一个按您键入的工作字段下拉搜索。当我从下拉列表中选择时,它愉快地将字段设置为我在搜索中收到的对象,但不高兴地注册该更改并将整个对象发送出去进行搜索。
.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()
以便我可以完成其他处理?
我前段时间遇到了同样的问题,这是我在系统中用于多个输入的最终解决方案。
步骤如下:
- 意识到这里实际上有两个事件。键入的内容和选择的值。
- 为这两个事件创建两个可观察量并分别处理它们。
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 }