37

I use resolvers to load the data into the components, but until the data is not returned from the server the component does not load. I would like to know if there is a way to render a load indicator before the server responds.

4 Answers 4

64

You can use the "reacting to routing events" strategy that consist to implement the App to react when any routing event occurs. To do that, you will need in your app.component.ts some code like:

import { Component } from '@angular/core';
import { Router, RouterEvent, NavigationStart, NavigationEnd, NavigationError, NavigationCancel } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.sass']
})
export class AppComponent {

  loading: boolean = true;

  constructor(private router: Router) {
    router.events.subscribe((routerEvent: RouterEvent) => {
      this.checkRouterEvent(routerEvent);
    });
  }

  checkRouterEvent(routerEvent: RouterEvent): void {
    if (routerEvent instanceof NavigationStart) {
      this.loading = true;
    }

    if (routerEvent instanceof NavigationEnd ||
      routerEvent instanceof NavigationCancel ||
      routerEvent instanceof NavigationError) {
      this.loading = false;
    }
  }
}

And in the view(html) something like:

<div id="loader" class="myloader" *ngIf="loading">
  Loading...
</div>
Sign up to request clarification or add additional context in comments.

5 Comments

This worked beautifully for me. I'm using a loader on several components that are already on the screen, but couldn't figure out how to to do it on a tabbed/route approach since the tab didn't show until the data was resolved. Using your code in app component like this solved the issue for me. :)
Best answer here. Thanks. This is a great solution rather than trying to wrap rest requests with progress indicators, e.g. in resolvers.
would use ResolveStart and ResolveEnd events instead
Also be sure to handle cancellation events if resolving fails
God bless you 😎
-2

the simplest version is going to be an ngIf statement

wrap an image in an ngIf on a root level component.

use a service to set whether it is visible or not.

eg:

before sending request: call service function to set variable to true

then in the component that is loaded, have the first thing that it does is set that variable back to false

Comments

-4

Another solution I came up with is using Observable<Observable<...>> in your resolvers:

// data-resolver.service.ts

// ...

export class DataResolverService implements Resolve<Observable<Data>> {

// ...

  resolve(route: ActivatedRouteSnapshot): Observable<Observable<Data>> {
    return of(this.client.get(...));
  }
}

Notice the usage of rxjs's of operator to construct an immediately completing Observable from your source Observable. Now you can easily handle your loading indicator in the component:

export class DashboardComponent {
  loading = true;
  error = false;

  data$: Observable<Data>;

  constructor(private activatedRoute: ActivatedRoute) {
    this.data$ = this.activatedRoute.data.pipe(switchMap((data) => data.data$));
    this.data$.subscribe(() => this.loading = false, () => this.error = true);
  }
}

See my in-depth medium story about this here. This is a real life example coming from my shift scheduling application tift.

5 Comments

This is terrible, if you are going to resolve the resolver sync to emit an observable, you can just as well skip the resolver and call the service directly from the component.
There is a good reason for why I propose this which I described in my story: of immediately completes and thus nicely plays with the Router waiting for the resolved Observable completing (which e.g. does not happen for remote calls etc).
> The Router does not finish navigating to the route until the data is resolved and only then your component is shown.
Resolvers have a very specific purpose, namely delaying navigation until some data is pre-loaded. If the resolver completes instantly and sync (like of does), you are effectively skipping the resolver, and all you do is adding the overhead of a resolver instead of using a normal service in your component. In your example you can delete the resolver, replace the first line in the constructor with this.data$ = this.client.get(...) and you have exactly the same with less code and overhead.
Thanks for your input. A few of the differences between resolvers and services/components are: You can reliably access (query) params in a resolver, whereas in components (thus in services) you have to subscribe to your route and potentially get empty (query) params in form of an observable. Resolvers make your application unreactive until the desired observable completes. Resolvers can be reused more declaratively across components. Resolvers have the clear responsibility of resolving data (not providing or displaying it). My examples are arguably a bit simple here.
-6

You should use https://www.npmjs.com/package/angular2-busy

which Directly listen to your Api call

ngOnInit() {
        this.busy = this.http.get('...').subscribe();
    }

<div [ngBusy]="busy"></div>

This will show spinner until you get response from server.

1 Comment

This is not the point at all in this question, the question relates to beeing busy in resolver, not in component.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.