3

I have some data that has to be loaded before the page loads and I'm using route resolvers to achieve this. Below is my code.

Service:

getUsernamesAndBUs(): Observable<any> {
    return Observable.forkJoin(
      this.http.get('http://localhost:8080/BMSS/getBusinessUnit'),
      this.http.get('http://localhost:8080/BMSS/getAllUser'),
      this.http.get('http://localhost:8080/BMSS/getCountry'),
      this.http.get('http://localhost:8080/BMSS/getCurrency')
    );
  }

Resolver:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { AgreementDetailsService } from './agreement-details.service';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class UsernamesAndBusResolveService implements Resolve<any> {

  constructor(private agreementDetailsService: AgreementDetailsService) { }

  resolve(route: ActivatedRouteSnapshot): Observable<any> {
    return this.agreementDetailsService.getUsernamesAndBUs();
  }
}

Routing Module:

{
    path: 'agreement',
    canActivate: [CanActivateAuthGuardService],
    children: [
      {
        path: 'create',
        component: AgreementComponent,
        resolve: { usernamesAndBUs: UsernamesAndBusResolveService }
      }
    ]
  }

In the component that's routed to:

this.businessUnits = this.route.snapshot.data['usernamesAndBUs'][0];
this.users = this.route.snapshot.data['usernamesAndBUs'][1];
this.countries = this.route.snapshot.data['usernamesAndBUs'][2];
this.currencies = this.route.snapshot.data['usernamesAndBUs'][3];

In interceptor:

intercept(httpRequest: HttpRequest<any>, httpHandler: HttpHandler): Observable<HttpEvent<any>> {
    if (!this.typeAheads.includes(httpRequest.url.split('/').pop())) {
      this.bmssLoaderService.start();
    } else {
      this.bmssLoaderService.stop();
    }


    return httpHandler.handle(httpRequest).do((httpEvent: HttpEvent<any>) => {
      if (httpEvent instanceof HttpResponse) {
        this.bmssLoaderService.stop();
      }
    }, (error: any) => {
      this.bmssLoaderService.stop();
    });
  }

I use a spinner (using HttpInterceptor) that shows up when back-end service is being called and disappears once the service returns. Now that the component takes long to load even after the back-end service has returned, the user is still on the same page for some time (around 10 seconds) after the spinner has disappeared, before routing to the target page.

I don't get what causes this delay and where am I wrong. Please help me with this.

karthikaruna
  • 3,042
  • 7
  • 26
  • 37
  • That's the whole point of a resolve: wait until the observable returned by the resolve has emitted, and the actual data is thus available, before routing to the component. The component can then use this data directly, without subscribing to an observable, because the router has subscribed and waited for the data to be available before activating the component. How could you access the data in the route snapshot if the router activated your component immediately? All your data would still be undefined. – JB Nizet Nov 03 '17 at 06:40
  • Ya, but what delays the observable from emitting? It takes around 10 seconds after the service has returned, to get routed to the target page. – karthikaruna Nov 03 '17 at 06:47
  • HTTP is asynchronous. You send a request to the server, and it takes some time to get the response. If you're waiting 10 seconds, it simply means the server needs 10 seconds to provide the response to at least one of the requests sent by the resolve. – JB Nizet Nov 03 '17 at 06:51
  • You could try `return this.agreementDetailsService.getUsernamesAndBUs().first();` – Günter Zöchbauer Nov 03 '17 at 07:02
  • @JBNizet by service, I meant the back-end REST service. The back-end service has returned, but it takes long time for the observable to emit. – karthikaruna Nov 03 '17 at 08:12
  • @GünterZöchbauer, that did't make any difference in the delay. – karthikaruna Nov 03 '17 at 08:13
  • 1
    I guess it's as JBNizet says. The observable returned from resolve, completes only when all requests have completed, and that is obviously not yet the case when you expect. – Günter Zöchbauer Nov 03 '17 at 08:17
  • 1
    Check your browser dev tools Network panel, to see when requests are sent and when the response actually comes back, and what it contains. – JB Nizet Nov 03 '17 at 08:20
  • Ya checked :) Noticed that the spinner disappears if at least one of the requests complete. Since I have 4 requests in forkJoin, routing happens only after all of them completes. – karthikaruna Nov 03 '17 at 08:26
  • @JBNizet how can I make the spinner disappear after all of the requests complete? – karthikaruna Nov 03 '17 at 08:29
  • Updated the question with my interceptor code – karthikaruna Nov 03 '17 at 08:34
  • @GünterZöchbauer, any ideas? – karthikaruna Nov 03 '17 at 08:48
  • 1
    I wouldn't use an HTTP interceptor to display a spinner. There are probably a bunch of requests for which you don't want such a spinner (typeahead request, background refresh requests, etc.). If you want to show a spinner when navigating and using a resolve, you should instead show a spinner when the router emits a navigation start event (or shortly after, to avoid a blinking effect), and hide it when it emits a navigation end event. – JB Nizet Nov 03 '17 at 08:56
  • 1
    If you really want to use an http interceptor, then you need to increment a counter when a request is sent, decrement it when the response is received, show the spinner when the counter goes from 0 to 1, and hide it when it goes from 1 to 0. – JB Nizet Nov 03 '17 at 08:56

0 Answers0