I like the idea of resolver
s.
You can say that:
I actually currently refactoring an app which uses a lot of resolvers, thought about them a lot and think the biggest issue with them is that they mutate the data, you have to map the data fetched from activatedRoute. In other words it adds complexities and problems in maintaining the app, whereas direct service injection doesn't have this issue.... Furthermore because resolvers are synchronous, in most cases the really slow down user experience...
Resolver: It gets executed even before the user is routed to new page.
Whenever you need to get the data before the component initialization, the right way to do this is to use resolver. Resolver acts synchronously i.e. resolver will wait for async call to complete and only after processing the async call, it will route to the respective URL. Thus, the component initialization will wait till the callback is completed. Thus, if you want to do something (service call), even before the component is initialized, you have come to right place.
Example Scenario: I was working on the project where user would pass the name of file to be loaded in the URL. Based on the name passed we would do the async call in ngOnInit and get the file. But the problem with this is, if user passes the incorrect name in URL, our service would try to fetch the file which does not exists on the server. We have 2 option in such scenario:
Option 1: To get the list of valid filenames in ngOnInit, and then call the actual service to fetch the file (If file name is valid). Both of these calls should be synchronous.
Option 2: Fetch the list of valid filenames in the resolver, check whether filename in URL is valid or not, and then fetch the file data.
Option 2 is a better choice, since resolver handles the synchronicity of calls.
Important:: Use resolver when you want to fetch the data even before the user is routed to the URL. Resolver could include service calls which would bring us the data required to load the next page.
The resolver gives you a hook near the start of router navigation and it lets you control the navigation attempt. This gives you a lot of freedom, but not a lot of hard and fast rules.
You don't have to use the result of the resolve in your component. You can just use the resolve as a hook. This is my preferred way of using it for the reasons you've cited. Using the result in your component is much simpler, but it has those synchronous trade-offs.
For example, if you're using something like Material or Cdk, you could dispatch a "loading" dialog to display a progress indicator at the start of the resolve, then close the dialog when the resolve concludes. Like so:
constructor(route: ActivatedRouteSnapshot, myService: MyService, dialog: MatDialog) {}
resolve() {
const dialogRef = this.dialog.open(ProgressComponent);
return this.myService.getMyImportantData().pipe(
tap(data => this.myService.storeData(data)),
tap(() => dialogRef.close()),
map(() => true),
catchError(err => of(false))
);
}
If you're using ngrx
, you could do something similar by dispatching actions while the resolve is in progress.
When you're using the Resolve guard in this fashion, there's not a particularly strong reason to use the Resolve guard instead of the CanActivate guard. That choice comes down to semantics. I find it more obvious to gate authentication on CanActivate and initiate data retrieval from Resolve. When those topics are allowed to blend it becomes an abritrary choice.
This is why I would use a resolver:
myapp.com/lists
, navigate to one element of the list myapp.com/lists/1
, showing the details of that element, no need to fetch the data, already done by the search. Then let suppose you navigate directly to myapp.com/lists/1
you need to fetch and then navigate to the componentThink to the resolver as a middleware between your app and your component, you can manage the loading view in the parent component, where <router-outlet>
is included.
I've been wondering the exact same thing.
The most intuitive discussion of this topic I've encountered is here: https://angular.schule/blog/2019-07-resolvers.
The author essentially boils it down to this: if you already use resolvers and don't have any UX issues, go for it. But most of the time, resolvers add unnecessary complexity and you're better off going with a reactive approach using a "smart containers" and "presentational components" structure. There are very few exceptions.
Using this structure, the smart components serve as a more dynamic form of resolver, and your presentation component handles the display of the pseudo-synchronous data.
As far as I can tell, resolvers are essentially a crutch for those who are less comfortable working with reactive patterns.