繁体   English   中英

如何让观察者作为可观察者返回?

[英]How to make an observer to return as observable?

我是rxjs的新手,想要解决这个问题。

我想将一个Observer传递给onAuthStateChanged() ,它接受一个观察者对象。 观察者会做一些工作并发出一个布尔值,以便布尔值可以作为Observable返回。 我如何实现从可观察到观察者的这个桥梁?

export class AuthGuard implements CanActivate {

constructor(private firebase: FirebaseService, private router: Router) {
}

canActivate(): Observable<boolean> {
    this.firebase.auth.onAuthStateChanged(/* an observer */)
    return /* an Observable<boolean> */
    }
}

由于onAuthStateChanged将一个观察者作为输入,并返回拆解函数,我们可以简单地用它包装:

Rx.Observable.create(obs => firebase.auth().onAuthStateChanged(obs))

实际上由于奇怪的原因,这可能不起作用,我们可以这样做:

var onAuthStateChanged$ = Rx.Observable.create(obs => {
  return firebase.auth().onAuthStateChanged(
    user => obs.next(user),
    err => obs.error(err),
    () => obs.complete());
})

现在,如果你不熟悉Observable.create函数,让我解释一下: create接受一个onSubscribe函数,该函数交给一个观察者并返回拆解函数。 对现在onAuthStateChanged听起来非常熟悉的是什么? 你交了nextOrObserver ,它就会返回拆解!

(现在由于奇怪的原因, nextOrObserver不接受我的observer ,所以我转而给它next函数.Hench上面的代码。)

设置onAuthStateChanged$后,我们可以使用运算符转换流。 所有操作符都将一个observable转换为另一个,而RxJs有几十个。 在您的情况下,它可能看起来像这样:

canActivate(): Observable<boolean> {
  onAuthStateChanged$
    .do(user => {if (!user) { this.router.navigate(['/login']); } })
    .map(user => !!user)
    .do(user => console.log('Authenticated?', user))
}

为了让别人受益,这就是我最后写的东西,它似乎运作良好。

import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';

import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { FirebaseService } from '../shared/firebase.service';


@Injectable()
export class AuthGuard implements CanActivate {

    loggedInSubject: ReplaySubject<any>;

    constructor(private firebase: FirebaseService, private router: Router) {
        this.loggedInSubject = new ReplaySubject(1);
        this.firebase.auth.onAuthStateChanged(this.loggedInSubject);
    }

    canActivate(): Observable<boolean> {
        return this.loggedInSubject.map(user => {
            if (!user) {
                this.router.navigate(['/login']);
            }
            console.log('Authenticated?', !!user);
            return !!user;
        }).take(1);
    }

}

这是短版本,你可以放在任何地方的辅助功能......

export function MakeAuthstateObservable(
  auth: firebase.auth.Auth
): Observable<firebase.User> {
  const authState = Observable.create((observer: Observer<firebase.User>) => {
    auth.onAuthStateChanged(
      (user?: firebase.User) => observer.next(user),
      (error: firebase.auth.Error) => observer.error(error),
      () => observer.complete()
    );
  });
  return authState;
}

类似方法:

./auth-guard.ts

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';

import { AuthService } from '../shared/auth.service';

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(
    private router: Router,
    private authService: AuthService) { }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {

    return this.authService.authState.map((auth) => {
      if (auth == null) {
        this.router.navigate(['auth']);
        return false;
      } else {
        return true;
      }
    }).first();

  }
}

./shared/auth.service.ts

import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import { FirebaseApp } from '../shared/firebase';

@Injectable()
export class AuthService {

  public auth: firebase.auth.Auth;
  public authState: Observable<firebase.User>;

  constructor(public app: FirebaseApp) {
    this.auth = app.auth();
    this.authState = this.authStateObservable(app);
  }

  /**
   * @function
   * @desc Create an Observable of Firebase authentication state
   */
  public authStateObservable(app: FirebaseApp): Observable<firebase.User> {
    const authState = Observable.create((observer: Observer<firebase.User>) => {
      this.auth.onAuthStateChanged(
        (user?: firebase.User) => observer.next(user),
        (error: firebase.auth.Error) => observer.error(error),
        () => observer.complete()
      );
    });
    return authState;
  }
}

./shared/firebase.ts

import * as firebase from 'firebase';

export class FirebaseApp implements firebase.app.App {

  name: string;
  options: {};
  auth: () => firebase.auth.Auth;
  database: () => firebase.database.Database;
  messaging: () => firebase.messaging.Messaging;
  storage: () => firebase.storage.Storage;
  delete: () => firebase.Promise<any>;

  constructor() {
    return firebase.initializeApp({
      apiKey: 'AIzaSyC6pDjAGuqXtVsU15erxVT99IdB0t4nln4',
      authDomain: 'inobrax-ebs-16552.firebaseapp.com',
      databaseURL: 'https://inobrax-ebs-16552.firebaseio.com',
      storageBucket: 'inobrax-ebs-16552.appspot.com',
      messagingSenderId: '383622803653'
    });
  }

}

不确定这是否必然比上面的答案“更好”,但它肯定更清洁。 我决定在AuthService上创建两个属性,一个只是一个布尔值来反映用户是否经过身份验证,一个userLoggedIn主题基本上发出了布尔属性的值。 这两个属性都与onAuthStateChanged()绑定。 因此,一旦状态发生更改,经过authenticated属性将变为true,否则userLoggedIn false, userLoggedIn使用next()next(this.authenticated) )发出此值。 AuthGuard我设置CanActivate()以返回booleanObservable<boolean> 首先,如果检查了AuthService上的authenticated属性,并且它返回true,否则它映射userLoggedIn主题以查明用户是否已经过身份验证。 这意味着在页面刷新后,guard将返回已发出主题的值,因为尚未定义authenticated ,因此只需等待userLoggedIn返回。 首先检查经过authenticated属性的原因是,如果您尝试使用nav链接更改页面,则不会发生任何事情,因为guard仅返回主题的发出值,仅在授权状态更改时调用 - 即登录,注销或页面刷新(重新引导应用程序)。 代码如下:

AuthService

import * as firebase from 'firebase';
import { Router } from '@angular/router';
import { Injectable, OnInit } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()

export class AuthService implements OnInit {
    authenticated: boolean;
    userLoggedIn = new Subject<boolean>();

    constructor(private router: Router) {}

    ngOnInit() {
    }

    checkAuthStatus() {
        firebase.auth().onAuthStateChanged((user) => {
            this.authenticated = !!user;
            this.userLoggedIn.next(this.authenticated);
        });
    }

    login(email: string, password: string) {
        firebase.auth().signInWithEmailAndPassword(email, password).then(() => {
            this.authenticated = true;
            this.router.navigate(['/']);
        }).catch((error) => {
            console.log(error);
        });
    }

    logout() {
        firebase.auth().signOut().then(function() {
            this.router.navigate(['login']);
        }.bind(this)).catch((error) => {
            console.log(error);
        });
    }
}

AuthGuard

import { CanActivate, Router } from '@angular/router';
import { Injectable } from '@angular/core';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs/Observable';

import 'rxjs/add/operator/map';

@Injectable()

export class AuthGuard implements CanActivate {
    constructor(private authService: AuthService, private router: Router) {
    }

    canActivate(): Observable<boolean> | boolean {
        if(this.authService.authenticated) {
            return true;
        }

        return this.authService.userLoggedIn.map((authenticated) => {
            if(!authenticated) {
                this.router.navigate(['login']);
            }

            return authenticated;
        });
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM