Angular2 - 扩展路由器和 Observable

Angular2 - Extending router and Observable

在带有路由器模块 v 3.0.0.6alpha 的 angular2 rc2 中,我扩展了 RouterOulet 以在访问管理员之前检查用户是否已登录。所以这是代码:

@Directive({
    selector: 'router-outlet'
})

export class LoggedInRouterOutlet extends RouterOutlet 
{
    publicRoutes: Array<string>;
    private parentOutletMap: RouterOutletMap;
    private userService: UserService;
    private parentRouter: Router;

    constructor(
        parentOutletMap: RouterOutletMap,
        _location: ViewContainerRef,
        @Attribute('name') name: string,
        userService: UserService,
        parentRouter: Router
    ) { 
        super(parentOutletMap, _location, name);

        this.parentRouter = parentRouter;
        this.parentOutletMap = parentOutletMap;
        this.userService = userService;
        this.publicRoutes = [
            'public', 
            'login'
        ];
    }

    activate(factory: ComponentFactory<any>, activatedRoute: ActivatedRoute, providers: ResolvedReflectiveProvider[], outletMap: RouterOutletMap) 
    {
        if (this._canActivate(factory.selector)) { 
            return super.activate(factory, activatedRoute, providers, outletMap); 
        }

        this.parentRouter.navigate(['/login']);
    }

    _canActivate(url) {
        return this.publicRoutes.indexOf(url) !== -1 || this.userService.isLoggedIn()
    }
}

userService.isLoggedIn() 必须 return 一个布尔值。我的问题是:如何调整我的代码以进行 HTTP 调用以检查用户是否已登录?因为如果 isLoggedIn 方法 return 一个可观察对象,并且我订阅了它,我就不能 return 父函数中的结果。

请注意OutletRouter activate 方法的结果已经改变。

@angular/router-deprecated

activate(nextInstruction: ComponentInstruction) : Promise<any>

@angular/router

activate(factory: ComponentFactory<any>, providers: ResolvedReflectiveProvider[], outletMap: RouterOutletMap) : ComponentRef<any>

这不再是 Promise 或 Observable。新的路由器实现带有一些不同的解决方案,我认为它更干净:Guards。

A guard's return value controls the router's behavior:

if it returns true, the navigation process continues if it returns false, the navigation process stops and the user stays put The guard can also tell the router to navigate elsewhere, effectively canceling the current navigation.

The guard might return its boolean answer synchronously. But in many cases, the guard can't produce an answer synchronously. The guard could ask the user a question, save changes to the server, or fetch fresh data. These are all asynchronous operations.

Accordingly, a routing guard can return an Observable and the router will wait for the observable to resolve to true or `false.

您可以创建 auth.guard.ts:

import { Injectable }             from '@angular/core';
import { CanActivate,
         Router,
         ActivatedRouteSnapshot,
         RouterStateSnapshot }    from '@angular/router';
import { UserService }            from './user.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private userService: UserService, private router: Router) {}

  canActivate(
    // Not using but worth knowing about
    next:  ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ) {
    return this.userService.isLoggedIn();
  }
}

现在确保您的 isLoggedIn return Observable(或 Promise - 尝试两者,因为 Angular2 参考是 not ready yet)。在我的例子中 API returns JSON 格式:{ success: true / false }.

public isLoggedIn() : Observable<boolean> | boolean {
    let router: Router = this.router;
    let obs;

    try {
        obs = this.authHttp.get('/api/check/logged')
            .map(result => result.json())
            .map(resultJson => (resultJson && resultJson.success));

    } catch (err) {
        obs = Observable.of(false);
    }

    return obs
        .map(success => {
             // navigate to login page
             if (!success)
                 router.navigate(['/auth/login']);

             return success;
        });
}

然后只需修改您的 RouterConfig 数组:

{ path: '/secret', component: SercetComponent, canActivate: [AuthGuard] }

另请参阅 Angular2 Developer Guide