user8116198
user8116198

Reputation:

Angular 6 | Only call a resolver once

I have a resolver on one of my routes in order to allow it to get data before the page actually loads. This required multiple requests therefore I am using rxjs function forkJoin. This returns the data great and the page doesn't load until all of the data is fully loaded, which is exactly how I want.

However, the data that is gets is pretty hefty meaning it can take a while before the route loads. I want to somehow, still resolve this route but then cache it and let the component know that this is cached.

I rely on the data coming back from the activatedRoute within the component as the page needs this. So I need it to stop overriding what is currently there as well.

This is my code so far:

Resolver:

const postId: string = this.postService.data.post.id;

const authorData$: Observable<HttpResponse<any>> = this.authorService.getAuthor();
const commentsData$: Observable<HttpResponse<any>> = this.commentsService.getComments();

I understand that this can be done in one request but this is just an example of what I would be using it for. So I don't want a suggestion that says put it in one request.

this.activatedRoute.data.subscribe((data) => {
   this.author = data[0][0];
   this.comments = data[0][1].data;
});

So I want to do this only on the first load of the component.

Thanks in advance.

Upvotes: 3

Views: 4440

Answers (1)

tom van green
tom van green

Reputation: 1734

One way of doing this is to introduce a replay subject in the resolver which will act as a cache. The resolver will always return this subject, but it will only request data, if it has not requested any data yet.

I made a simple example where the resolver only has to return a number, which it gets from a simple data service.

class Resolver implements Resolve<number> {
    // We need to keep track on if a request has already been sent
    requested = false;
    // The replay subject will emit the last value that has been passed in
    subject = new ReplaySubject<number>(1);

    resolve(route: ActivatedRouteSnapshot): Observable {
        // Request data only if it has not been requested yet
        if (!this.requested) {
            this.requested = true;
            
            // Do your heavy call here
            this.service.getData()
                // Emit your data into the subject
                .subscribe(nr => this.subject.next(nr));
        }

        // Return the subject. Pipe in first() because resolvers 
        // only emit on completion of an observable.
        return this.subject.pipe(first());
    }

}

You should be able to do the same. If you have multiple calls you can either use forkJoin or similar to execute both calls and supply the result to the replay subject. Or you could create two separate resolvers, which are only responsible for one result object each.

Upvotes: 10

Related Questions