angulartypescriptrxjsangular-ui-bootstrapangular-bootstrap

Angular is not updating view with boolean variable in the rxjs pipe


I have an issue with the Angular 14 which is not updating the view when querying data.

I want to show a loading spinner in my HTML when data are being loaded from the server. I am using ngbTypeahead for suggestions/hints. When I set the boolean value in the function, it does not show anything in the view. I tried using NgZone and run method for Angular force view update but seems not working for me.

Here is the TS

  public isLoadingVisible: boolean = false

  public suggestStations: OperatorFunction<string, Array<Station>> = (text$: Observable<string>) => {
    this.zone.run(() => this.isLoadingVisible = true)
    const suggestion = text$.pipe(
      debounceTime(200),
      distinctUntilChanged(),
      switchMap((searchText: string) => this.dataService.suggestStations(searchText.toLowerCase())),
    );
    this.zone.run(() => this.isLoadingVisible = false)
    return suggestion
  }

// Also tried to put loading into the switchMap function, nut not working as well

  public suggestStations: OperatorFunction<string, Array<Station>> = (text$: Observable<string>) => {
   return text$.pipe(
      debounceTime(200),
      distinctUntilChanged(),
      switchMap((searchText: string) => {
        this.zone.run(() => this.isLoadingVisible = true)
        const result = this.dataService.suggestStations(searchText.toLowerCase())
        this.zone.run(() => this.isLoadingVisible = false)
        return result
      }),
    );
  }


HTML View

<p *ngIf="isLoadingVisible">Loading...</p>


      <div class="input-group mb-4">
        <input [(ngModel)]="selectedStation"
               [editable]='false'
               [inputFormatter]="formatter"
               [ngbTypeahead]="suggestStations"
               [resultFormatter]="formatter"
               [resultTemplate]="station_row"
               class="form-control p-2"
               placeholder="Název stanice"
               type="text">
      </div>

Do you please have any idea, where might be the issue? Thanks a lot for your help!


Solution

  • It looks like ngbTypeahead internally subscribes to suggestStations. What you have in both cases is incorrect because you don't wait for the inner Observable to complete or you change isLoadingVisible outside of the chain.

    So what you should do is putting isLoadingVisible into the chain and make sure you trigger change detection yourself.

    constructor(
      private cdr: ChangeDetectorRef,
    ) {}
    
    ...
    
    public suggestStations: OperatorFunction<string, Array<Station>> = (text$: Observable<string>) => {
      return text$.pipe(
        tap(() => {
          this.isLoadingVisible = true;
          this.cdr.markForCheck();
        }),
        debounceTime(200),
        distinctUntilChanged(),
        switchMap((searchText: string) => this.dataService.suggestStations(searchText.toLowerCase())),
        tap(() => {
          this.isLoadingVisible = false;
          this.cdr.markForCheck();
        }),
      );
    }