Angular2, Firebase, AngularFire2, Route Guard 以及如何处理订阅

Angular2, Firebase, AngularFire2, route guard and how to deal with subscription

我有一个目前可用的路由守卫,但我收到此消息:

WARNING: the getAuth() API has changed behavior since adding support for Firebase 3.
    This will return null for the initial value when the page loads, even if the user is actually logged in.
    Please observe the actual authState asynchronously by subscribing to the auth service: af.auth.subscribe().
    The getAuth method will be removed in future releases

这是我现在的守卫:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AngularFire, FirebaseListObservable, AuthProviders, AuthMethods } from 'angularfire2';
import {Observable} from "rxjs/Rx";

@Injectable()
export class AuthFirebaseGuard implements CanActivate {

  user={}
  constructor(public af: AngularFire,
              private router: Router
              ) {}

  canActivate(route: ActivatedRouteSnapshot,
              state: RouterStateSnapshot){

    if (this.af.auth.getAuth().uid){
        return true;
    } else {
        this.router.navigate(['login']);
        return false;
    }

  }
}

这里是我想改成的:

@Injectable()
export class AuthFirebaseGuard implements CanActivate {

  user={}
  constructor(public af: AngularFire,
              private router: Router
              ) {}

  canActivate(route: ActivatedRouteSnapshot,
              state: RouterStateSnapshot){


    this.user = this.af.auth.subscribe(user => {
              if (user) {
                        this.user = user.auth.providerData[0];
                        if (user.auth.emailVerified==false){
                        user.auth.sendEmailVerification().then(function() {
                            this.router.navigate(['login']);
                            return false;
                        });
                        } else {
                           return true;
                        }
              }
              else {
                this.router.navigate(['login']);
                return false;
              }
            }); 
  }
}

如何等待订阅完成?

Class 'AuthFirebaseGuard' incorrectly implements interface 'CanActivate'.
  Types of property 'canActivate' are incompatible.
    Type '(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => void' is not assignable to type '(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => boolean | Observable<boolean> | Pr...'.
      Type 'void' is not assignable to type 'boolean | Observable<boolean> | Promise<boolean>'.








ERROR in [default] /Users/Documents/frontend/iboot/src/app/common/auth-firebase.gaurd.ts:14:13 
Class 'AuthFirebaseGuard' incorrectly implements interface 'CanActivate'.
  Types of property 'canActivate' are incompatible.
    Type '(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable<{}>' is not assignable to type '(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => boolean | Observable<boolean> | Pr...'.
      Type 'Observable<{}>' is not assignable to type 'boolean | Observable<boolean> | Promise<boolean>'.
        Type 'Observable<{}>' is not assignable to type 'Promise<boolean>'.
          Property 'then' is missing in type 'Observable<{}>'.

ERROR in [default] /Users/Documents/frontend/iboot/src/app/common/auth-firebase.gaurd.ts:41:40 
Argument of type 'firebase.Promise<any>' is not assignable to parameter of type 'Promise<any>'.
  Types of property 'then' are incompatible.
    Type '(onResolve?: (a: any) => any, onReject?: (a: Error) => any) => Promise<any>' is not assignable to type '{ <TResult1, TResult2>(onfulfilled: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected:...'.
      Type 'firebase.Promise<any>' is not assignable to type 'Promise<any>'.

canActivate 可以 return 布尔值、promise 或 observable,因此您可以使用 AngularFire2 的 auth observable 组合一个 observable:

canActivate(
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
) {

  return this.af.auth

    // Observables returned by canActivate have to complete, so take the
    // first emitted value from the auth observable.

    .first()

    // Use mergeMap to project the values emitted from the inner
    // observables created using Observable.fromPromise and
    // Observable.of. (If you weren't making the call to
    // sendEmailVerification, you could just use the map operator
    // and would return a boolean.)

    .mergeMap((user) => {

      if (user && user.auth) {
        if (!user.auth.emailVerified) {
          return Observable.fromPromise(user.auth
            .sendEmailVerification()
            .then(() => {
              this.router.navigate(['login']);
              return false;
            })
          );
        }
        return Observable.of(true);
      } else {
        this.router.navigate(['login']);
        return Observable.of(false);
      }
    });
}