【问题标题】:How to make an observer to return as observable?如何使观察者返回可观察?
【发布时间】:2016-08-23 23:52:01
【问题描述】:

我是 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 的函数,使用replaySubject 来记住该主题发出的最后一个值,在您的函数中使用该主题发出一些布尔值。然后返回相同的主题。主体既是可观察者又是观察者。所以主题将从函数中接收到值。作为一个 observable,其他侦听器可以订阅它,并取回该值。因为您使用了replaySubject,所以即使您的听众在主题发出后订阅该主题,他们仍然能够取回该值。

标签: firebase rxjs firebase-authentication


【解决方案1】:

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

Rx.Observable.create(obs =&gt; 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$,我们可以使用运算符转换流。所有操作员所做的就是将一个可观察对象转换为另一个,而 RxJs 有几十个。在您的情况下,它可能如下所示:

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

【讨论】:

    【解决方案2】:

    为了造福他人,这就是我最终写的,它似乎运作良好。

    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);
        }
    
    }
    

    【讨论】:

    • "onAuthStateChanged 失败:第一个参数 "nextOrObserver" 必须是有效的对象或函数。"
    【解决方案3】:

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

    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;
            });
        }
    }
    

    【讨论】:

      【解决方案4】:

      这是一个简短的版本,一个可以放置在任何地方的辅助函数...

      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;
      }
      
      

      【讨论】:

        【解决方案5】:

        类似的方法:

        ./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'
            });
          }
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-17
          • 2014-03-24
          • 2011-06-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-26
          相关资源
          最近更新 更多