Loading Controllers with APIs and Observables

Hi all, in my mission to embrace observables and extension from this post, I am writing to see how everyone would go about implementing a loading controller on fetch.

As a working example say I have an API fetch users in a service.

export class UserService {

    constructor(
        public http: Http
    ) {}

    public fetchUsers() {
        return this.http.get("/api/users").map((res: Response) => res.json())
    }
}

In my component page I have an observable that fetches new users on init.

export class UserList {

    public users$: Observable<Users[]>

    constructor(
        public userService: UserService,
    ) 
    public ngOnInit() {
        this.users$ = this.userService.fetchUsers()
    }



}

The question I am asking how would you implement a loading controller(ie a spinner wheel saying it’s loading) to start when fetching users begins. And then dismissed once completed. Is it possible using the observable method?

I’m not generally a huge fan of Ionic’s loading spinners, because they tend to block all user interaction, but I’ll share the strategy that I use for this overall purpose, which should be easily adaptable to popping loading indicators if you wish:

@Injectable()
export class NetmonService implements HttpInterceptor {
  private reqcnt = 0;
  private active$ = new BehaviorSubject<boolean>(false);

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.reqcnt++;
    if (!this.active$.value) {
      this.active$.next(true);
    }
    return next.handle(req).pipe(finalize(() => {
      this.reqcnt--;
      if (this.reqcnt === 0) {
        this.active$.next(false);
      }
    }));
  }

  watchActive(): Observable<boolean> {
    return this.active$;
  }
  
  isActive(): boolean {
    return this.active$.value;
  }
}

The way LoadingController works in Ionic 4, you should be able to interact with it directly from inside NetmonService if desired, or particular pages could inject NetmonService and use watchActive to figure out when to do that.

It’s basically a reference counter on active HTTP requests that is transparent to all users, so no other services that are firing API requests have to know about it, do anything to support it, or even care whether or not it exists. It must be registered like any other HttpInterceptor. I do this in app.module.ts like so:

export const netmon = new NetmonService();
...
providers: [
{
  provide: HTTP_INTERCEPTORS,
  useValue: netmon,
  multi: true,
},
]

In order to work properly, the same netmon object must see all traffic, so it absolutely must be an app-wide singleton, so getting cute with putting it in lazily-loaded modules will defeat its purpose. That’s also why I instantiate it manually and use useValue instead of letting DI instantiate it on demand.

1 Like

Thanks again for the prompt response and solution. To clarify, this intercepts all HTTP requests? And when any HTTP is making a call active$ will be true. And when the request is false, active$ will be false?

Does that mean if I have a page on init running multiple fetches to seperate APIs as it is intercepting all HTTP requests, isActive will remain true until the last HTTP request call is finished? Not a bad a thing at all on my part but just trying the behaviour.

All that go through the Angular HttpClient, yes. It wouldn’t see, for example, stuff going through Ionic-Native HTTP.

Yes and yes again. Any other solution would probably have to at least think about what to do in that case, where you have two or more outstanding network requests at once.

1 Like