【问题标题】:Angular Material, Autocomplete loading indicator and showing text (no results found)Angular 材质、自动完成加载指示器和显示文本(未找到结果)
【发布时间】:2020-02-12 16:08:17
【问题描述】:

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

我有以下 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 =&gt; this.to.filter(term))里面的请求已经完成,所以我将加载变量设置为false?

我的第二个问题是如何在服务器返回一个空数组时显示未找到的注册消息,因为我正在使用异步。

【问题讨论】:

    标签: angular rxjs angular-material


    【解决方案1】:

    我曾经做过类似的事情,我已经尝试过适应你的需要,所以就这样吧。

    从服务开始,我们需要设置一个加载流来观看。

    // 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

    【讨论】:

    • 这太棒了!不过,您可能希望禁用这些额外选项,以免它们被选中。
    • @adam0101 非常正确。固定!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多