I'm using route guards, specifically the canActivate()
method, but Angular is calling ngOnInit()
of my root AppComponent
before canActivate
is called.
I have to wait on some data in canActivate
before the AppComponent
can render it in the template.
How can I do this?
I was dealing with such cases, and here is what I usually do:
1. I create a Resolver service (which implements Resolve
interface). It allows you to get all necessary data before activating the route:
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { DataService } from 'path/to/data.service';
@Injectable()
export class ExampleResolverService implements Resolve<any> {
constructor(private _dataService: DataService) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<any> {
return this._dataService.anyAsyncCall()
.then(response => {
/* Let's imagine, that this method returns response with field "result", which can be equal to "true" or "false" */
/* "setResult" just stores passed argument to "DataService" class property */
this._dataService.setResult(response.result);
})
.catch(err => this._dataService.setResult(false););
}
}
2. Here is how we can deal with AuthGuard, which implements CanActivate
interface:
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { DataService } from 'path/to/data.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private _dataService: DataService) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
/* "getResult" method operates with the same class property as setResult, it just returns the value of it */
return this._dataService.getResult(); // will return "true" or "false"
}
}
3. Then you can include the Resolver and the AuthGuard to your routes config, here is just a part (the structure of routes can differ, here is an example with activating the parent component):
const routes: Routes = [
{
path: 'app',
component: AppComponent,
resolve: {
result: ExampleResolverService // your resolver
},
canActivate: [AuthGuard], // your AuthGuard with "canActivate" method
children: [...] // child routes goes inside the array
}
];
When you're navigating to /app
, the ExampleResolverService
starts, makes API call and stores the necessary part of response to class property in DataService
via setResult
method (it's the usual setter). Then, when the resolver finished the work, it's time for our AuthGuard
. It gets stored result from DataService
via getResult
method (it's the usual getter), and returns this boolean result (our AuthGuard
expects boolean to be returned, and the route will be activated if it returns true
, and will not be activated if it returns false
);
It's the simplest example without any additional operations with data, the logic is more complex usually, but this skeleton should be enough for basic understanding.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With