为什么在 canActivate() 之前调用 ngOnInit()?
Why is ngOnInit() being called before canActivate()?
我正在使用路由守卫,特别是 canActivate()
方法,但是 Angular 正在调用我的根 AppComponent
之前的 ngOnInit()
canActivate
被调用。
我必须等待 canActivate
中的某些数据,然后 AppComponent
才能在模板中呈现它。
我该怎么做?
我正在处理这种情况,这是我通常做的事情:
1. 我创建了一个 Resolver 服务(实现了 Resolve
接口)。它允许您在激活路线之前获得所有必要的数据:
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. 下面是我们如何处理 AuthGuard,它实现了 CanActivate
接口:
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. 然后你可以将 Resolver 和 AuthGuard 添加到你的路由配置中,这里只是一部分(路由的结构可以不同,这里以激活父组件为例):
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
}
];
工作原理
当您导航到 /app
时,ExampleResolverService
开始,进行 API 调用并存储对 class 属性 的必要响应部分在 DataService
中通过 setResult
方法(这是通常的 setter)。然后,当解析器完成工作时,就到了我们的 AuthGuard
的时候了。它通过 getResult
方法(这是通常的 getter)从 DataService
获取存储结果,以及 returns 这个布尔结果(我们的 AuthGuard
期望返回布尔值, returns true
会激活路由 returns false
);
不会激活
这是最简单的例子,没有对数据进行任何额外的操作,逻辑通常更复杂,但是这个框架应该足够基本理解了。
对我来说,我在我的应用程序组件中监听了 ROUTE_NAVIGATED 事件,如下所示
我正在使用 ngrx/router-store 来收听这些路由器操作。
// app.component.ts
public ngOnInit(): void {
// grab the action stream
this.actions$.pipe(
// Only pay attention to completed router
ofType(ROUTER_NAVIGATED),
// Now I can guarantee that my resolve has completed, as the router has finsihed
// Switch
switchMap(() => {
// Now switch to get the stuff I was waiting for
return this.someService.getStuff();
})
// Need to subscribe to actions as we are in the component, not in an effect
// I suppose we should unsubscribe, but the app component will never destroy as far as I am aware so will always be listening
).subscribe();
我正在使用路由守卫,特别是 canActivate()
方法,但是 Angular 正在调用我的根 AppComponent
之前的 ngOnInit()
canActivate
被调用。
我必须等待 canActivate
中的某些数据,然后 AppComponent
才能在模板中呈现它。
我该怎么做?
我正在处理这种情况,这是我通常做的事情:
1. 我创建了一个 Resolver 服务(实现了 Resolve
接口)。它允许您在激活路线之前获得所有必要的数据:
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. 下面是我们如何处理 AuthGuard,它实现了 CanActivate
接口:
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. 然后你可以将 Resolver 和 AuthGuard 添加到你的路由配置中,这里只是一部分(路由的结构可以不同,这里以激活父组件为例):
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
}
];
工作原理
当您导航到 /app
时,ExampleResolverService
开始,进行 API 调用并存储对 class 属性 的必要响应部分在 DataService
中通过 setResult
方法(这是通常的 setter)。然后,当解析器完成工作时,就到了我们的 AuthGuard
的时候了。它通过 getResult
方法(这是通常的 getter)从 DataService
获取存储结果,以及 returns 这个布尔结果(我们的 AuthGuard
期望返回布尔值, returns true
会激活路由 returns false
);
这是最简单的例子,没有对数据进行任何额外的操作,逻辑通常更复杂,但是这个框架应该足够基本理解了。
对我来说,我在我的应用程序组件中监听了 ROUTE_NAVIGATED 事件,如下所示
我正在使用 ngrx/router-store 来收听这些路由器操作。
// app.component.ts
public ngOnInit(): void {
// grab the action stream
this.actions$.pipe(
// Only pay attention to completed router
ofType(ROUTER_NAVIGATED),
// Now I can guarantee that my resolve has completed, as the router has finsihed
// Switch
switchMap(() => {
// Now switch to get the stuff I was waiting for
return this.someService.getStuff();
})
// Need to subscribe to actions as we are in the component, not in an effect
// I suppose we should unsubscribe, but the app component will never destroy as far as I am aware so will always be listening
).subscribe();