Angular2 - Extending router and Observable

2019-02-18 17:44发布

In angular2 rc2 with the router module v 3.0.0.6alpha, I extend the RouterOulet to check if the user is logged in before accessing the admin. So this is the code :

@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() has to return a boolean. My question is : How do I adapt my code to make an http call to check if the user is logged in ? Because if the isLoggedIn method return an observable object, and I subscribe it, I can't return the result in the parent function.

1条回答
做自己的国王
2楼-- · 2019-02-18 18:04

Please notice that the result of activate method of OutletRouter has changed.

@angular/router-deprecated

activate(nextInstruction: ComponentInstruction) : Promise<any>

@angular/router

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

which is not a Promise or Observable any more. New router implementation comes with a bit different solution that I think is cleaner: 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.

You can create 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();
  }
}

Now make sure your isLoggedIn return Observable (or Promise - try both as Angular2 Reference is not ready yet). In my case API returns JSON in format: { 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;
        });
}

Then just modify your RouterConfig array:

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

Refer also to Angular2 Developer Guide

查看更多
登录 后发表回答