我需要在搜索数据时显示加载,并在未找到记录时显示文本。
我有以下 html 摘录:
<input
matInput
[matAutocomplete]="auto"
[formControl]="formControl"
[formlyAttributes]="field"
[placeholder]="to.placeholder"
[errorStateMatcher]="errorStateMatcher"
/>
<mat-icon class="arrow-autocomplete">arrow_drop_down</mat-icon>
<mat-autocomplete
#auto="matAutocomplete"
[displayWith]="displayFn.bind(this)">
<mat-option *ngIf="isLoading" class="is-loading">
<mat-spinner diameter="25"></mat-spinner>
</mat-option>
<ng-container *ngIf="!isLoading">
<mat-option *ngFor="let value of result$ | async" [value]="value">
<ng-container *ngFor="let ky of to.filterKeys; let index = index">
{{
index !== to.filterKeys.length - 1
? value[ky] + ' - '
: value[ky]
}}
</ng-container>
<ng-container *ngIf="!to.filterKeys">
{{ value }}
</ng-container>
</mat-option>
</ng-container>
</mat-autocomplete>
Component.ts 代码:
ngOnInit(): void {
super.ngOnInit();
this._unsubscribeAll = new Subject();
this.isLoading = false;
this.result$ = this.formControl.valueChanges.pipe(
takeUntil(this._unsubscribeAll),
debounceTime(300),
startWith(''),
tap(() => {
this.isLoading = true
}),
switchMap(term => this.to.filter(term))
);
}
对于加载,我会用
tap()
控制它方法,但我的问题是如何知道 switchMap(term => this.to.filter(term))
里面的请求完成了,所以我将加载变量设置为false?我的第二个问题是如何在服务器返回空数组时显示未找到的注册消息,因为我正在使用异步。
最佳答案
我曾经做过类似的事情,我试图让它适应你的需要,所以这里是。
从服务开始,我们需要设置一个加载流来观看。
// Loading stream
private readonly loading = new Subject<boolean>();
get loading$(): Observable<boolean> {
return this.loading;
}
constructor(private http: HttpClient) {}
get(q: string) {
return this.http.get<IResults>(URL + q).pipe(
// Set loading to true when request begins
tap(() => this.loading.next(true)),
// If we get to this point, we know we got the data,
// set loading to false, return only the items
map((res) => {
this.loading.next(false);
return res.items;
})
)
}
// Cleanup.
ngOnDestroy() {
this.loading.unsubscribe();
}
在这个例子中,预期的数据是
interface IResults {
total_count: number,
incomplete_results: boolean,
items: []
}
所以我们使用 tap 让每个人都知道它正在加载,然后让每个人都知道我们在 map() 中完成了。这解决了一半的困难。
接下来,component.ts 非常简单,我们只是在观察 observables。
// Form
searchForm = new FormGroup({
query: new FormControl('')
})
// Get loading stream from service
loading$: Observable<boolean> = this.gs.loading$;
// Deconstruct form to just take the query
// Search on changes
searchResults$ = this.searchForm.valueChanges.pipe(
switchMap(({query}) => this.gs.get(query))
);
constructor(private gs: GithubService) { }
现在我们可以在 html 中完成它。
<!-- ngIf so my http request is only called once-->
<form [formGroup]="searchForm"
*ngIf="{results: searchResults$ | async, loading: loading$ | async} as obs">
<!-- Search input -->
<mat-form-field appearance="legacy">
<input matInput [matAutocomplete]="autoComplete" formControlName="query">
<mat-icon matSuffix>arrow_drop_down</mat-icon>
<mat-hint>Search Github Users</mat-hint>
</mat-form-field>
<!-- Auto Complete -->
<mat-autocomplete #autoComplete="matAutocomplete">
<!-- If we're loading -->
<mat-option disabled class="loading" *ngIf="obs.loading">
<mat-spinner diameter="35"></mat-spinner>
</mat-option>
<!-- If we're not loading AND the array length is 0, show this -->
<mat-option disabled *ngIf="obs.results?.length === 0 && !obs.loading">
No user found
</mat-option>
<!-- Actual payload -->
<ng-container *ngIf="!obs.loading">
<mat-option *ngFor="let result of obs.results">
{{result.login}}
</mat-option>
</ng-container>
</mat-autocomplete>
</form>
我们将 ngif 放在表单中以避免在同一组件中发出多个 GET 请求,并将其放在我们可以引用的对象中。
将自动完成功能连接到输入。
现在我们可以添加几个有条件的(数组的长度以及我们是否正在加载)。
我还创建了一个 stackblitz 来测试这一切。挺好玩的!
https://stackblitz.com/github/baxelson12/ng-mat-autocomplete
关于Angular Material、自动完成加载指示器和显示文本(未找到结果),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60192550/