2

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!

Petr Jelínek
  • 1,259
  • 1
  • 18
  • 36

2 Answers2

2

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();
    }),
  );
}
martin
  • 93,354
  • 25
  • 191
  • 226
0

I used something like this

pipe(tap(() => this.loading.showLoading()),
    finalize(() => this.loading.hideLoading())

where loading is and injected class to handle globally this variable. I executed outside zone in order not to trigger the refresh automatically and used the setTimeout with time zero to run one view refresh (not super clean code maybe but was working)

@Injectable({ providedIn: 'root' })
export class Loading{
   showLoading(): void {
      this.zone.runOutsideAngular(() => {
         setTimeout(() => {
            this._isLoading$.next(true);
         }, 0);
      });
   }
}

hope it can help or give you some new ideas

G.D.
  • 75
  • 9