0

If an Angular2 template depends on data in order to display, and that data hasn't been loaded yet, it will cause the entire app to break, requiring the user reload his page.

Angular2 resolvers are the solution to this problem. Resolvers prevent pages from loading until their promise or observable resolves. But the problem is that the page won't be rendered at all until the resolver resolves.

What happens if you want to have the page displayed, for example, behind a spinner, and just have the data appear once it's available? Is there a way to do this in Angular2 that still uses resolvers, or is the only way to implement your own service calls in the component, with logic in the template to prevent data from being used if it isn't available? For example, every {{ foo.bar }} in your templates would have to be changed to {{ foo && foo.bar }}.

Is there an Angular2-friendly approach to doing this?

A. Duff
  • 4,097
  • 7
  • 38
  • 69

2 Answers2

1

Assumed, you resolve your data within your component, you don't need to double check like

{{foo && foo.bar}}

Just use the question mark operator, which is handy if you want to resolve nested properties and each one of which might be undefined:

{{foo?.bar?.another?.property}}

However, you might quickly find yourself using tons of ? in your HTML. That's why using the ? might be a little ugly for data that are not yet resolved.

Another solution is to wrap everything in an ngIf like this

 <div *ngIf="loading">Page is loading</div>
 <div *ngIf="!loading"> {{foo.bar}} </div>

On the other hand, if you have multiple components, you might have many spinner icons, loading... messages spread all over your page. Or you just place your ngIf in the top level component of your particular content area. I'd say, it depends on your use case and your personal UX preferences.

Jan B.
  • 6,030
  • 5
  • 32
  • 53
  • Thanks. My question is mainly about how to do these sorts of things (multiple spinners, page displaying) while maintaining the resolver pattern. Otherwise, you need to add custom logic in your component to determine if there data is loaded yet. I'm asking how to display the page without having to do that. – A. Duff Jan 19 '17 at 19:26
  • I don't think that this is possible. Quoting "For example, in a contacts application where we’re able to click on a contact to view a contact’s details, the contact data should’ve been loaded before the component we’re routing to is instantiated" (https://blog.thoughtram.io/angular/2016/10/10/resolving-route-data-in-angular-2.html) – Jan B. Jan 19 '17 at 19:50
  • If the component does not get instantiated before the Resolver is done, there is no chance to render the component's view. I guess that's one of the goals of a Resolver, anyway. – Jan B. Jan 19 '17 at 19:52
1

Resolvers prevent pages from loading until their promise or observable resolves. But the problem is that the page won't be rendered at all until the resolver resolves.

To be more specific, a resolve prevents a route from being activated. But some parts of the page/UI are still loaded (and possibly rendered) as the router can't control 100% of the view.

At the very least, you still have the root component, aka AppComponent, that wraps the <router-outlet></router-outlet>.

You could put the spinner's markup inside AppComponent's template and use a service to start/stop it.

Then, inject this service in your Resolve to coordinate the spinner and the async operation. More specifically, inside the resolve() method you could:

  • Start the spinner.
  • Attach a "stop spinner" callback to the onComplete and onError events (assuming you're using observables).
  • Return the observable for the async operation (e.g. HTTP call).

How your spinner is displayed depends on which CSS framework you use. It could be an overlay that dims the full screen (kind of like a modal) or a small notification in a corner of the screen.

Further improvement: Why not spy on the Http service (or use a custom centralised service to wrap all your HTTP calls) so that you can start and stop the spinner whenever there's an HTTP call in progress (provided this is the desired behavior...). That way, you wouldn't have to handle the manual starting and stopping of the spinner in each and every one of your resolves.

AngularChef
  • 13,797
  • 8
  • 53
  • 69