Angular Material、自动完成加载指示器和显示文本(未找到结果)

标签 angular rxjs angular-material

我需要在搜索数据时显示加载,并在未找到记录时显示文本。

我有以下 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/

相关文章:

angular - 使用 ngif 在 mat-table 中隐藏一行

javascript - 使用 JavaScript 映射和缩减 JSON 对象

angular - 嵌套数组的 RXJS 运算符

javascript - RxJS5 中 shareReplay(1) 的模式

angular - 垫菜单中的 xPosition 和 yPosition 不起作用

angular - Firebase Angularfire2 检查用户是否存在于数据库中

Angular 2 弹性布局高度 100%

javascript - 我可以在 mat-expanded 行中显示另一个表格吗?

Angular 设置 HTTP 超时

angular - 如何使用 Angular 为 2 的 Angular Material ?