【问题标题】:Combine RxJS Observable in Angular在 Angular 中结合 RxJS Observable
【发布时间】:2018-12-03 22:56:25
【问题描述】:

问题:如果 firebase-auth 没有用户登录,我需要路由到登录页面;如果有用户登录,我需要路由到备用页面。

为了实现这一点,它将调用构造函数服务中的一个函数。

private isLoginRequired() {
// Firebase auth service observable
let authSub = this.afAuth.authState;

// Current route service observable
let routeSub = this.activatedRoute.url;

//Alternate App Page
//this.router.navigate(["tool/event-management/events"]);

//Login Page
//this.router.navigate(["tool/event-management/login"]);

}

现在,我需要以这样的方式组合这些 observables,如果其中任何一个的值发生变化,我可以通过单个订阅收集两个 observables 的当前值。

something.subscribe(observableSnapshots => {
  let snapshot_auth = observableSnapshots[0];
  let snapshot_route = observableSnapshots[1];

  if(condition<snapshot_route, snapshot_auth>){
    // do anything positive
  }
  else{
    // do anything negative
  }
});

我知道这可以通过嵌套订阅来实现,但这肯定不是一种有效的方法。

我也尝试过mergeMap但无法达到要求,这里是sn-p(不起作用)

routeSub
      .pipe(mergeMap(event1 => authSub.pipe(map(event2 => "test"))))
      .subscribe(result => {
        console.log(result);
      });

请告诉我一个更好的出路。提前致谢。

依赖:“rxjs”:“~6.3.3”

【问题讨论】:

  • 你不应该订阅你的内心,而应该订阅你的外在..
  • @MikeOne 我试过交换它们并没有多大帮助。
  • 您是否研究过 Angular 中的路由器防护?他们可以在用户登陆页面之前处理身份验证逻辑。
  • @sketchthat,如果用户已经在页面上,并且从第二个选项卡触发了锁定该页面的事件(例如注销)怎么办?
  • 这是一篇文章,展示了在 Angular 中合并可观察对象的方法的可视化。它值得一看。 Learn to combine RxJs sequences with super intuitive interactive diagrams

标签: angular firebase rxjs


【解决方案1】:

我会使用 路由守卫 来做你所描述的事情:

这是来自 Jeff Delaney 的 AngularFirebase 网站的稍微修改的代码 sn-p,请参阅:https://angularfirebase.com/lessons/google-user-auth-with-firestore-custom-data/

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AngularFireAuth } from '@angular/fire/auth';
import { Observable } from 'rxjs';
import { tap, map, take } from 'rxjs/operators';

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


  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> {

      return this.auth.authState.pipe()
           take(1),
           map(user => !!user),
           tap(loggedIn => {
             if (!loggedIn) {
               console.log('access denied')
               this.router.navigate(['/login']);
             }
         })
    )
  }
}

以下是如何将保护添加到路由器:

const routes: Routes = [
  { path: '**', component: SomeComponent, canActivate: [AuthGuard]},
]

您可以像我在这里所做的那样将 Firebase auth observable 导入警卫中,但是,最好将它放在自己的服务 imo 中。检查指向 AngularFirebase 的链接以查看执行此操作的服务的代码。

如果您想在用户注销后将其从路由中抛出,请在您的组件中尝试以下代码:

export class SomeComponent implements OnInit {
        constructor(private auth: AngularFireAuth, private router: Router)
{
    ngOnInit(): void {
        this.auth.authState.subscribe((authObj) => {
            if(authObj === null){
                this.router.navigate(['/login']);
            }
        })
    }
}

【讨论】:

    【解决方案2】:

    我不能 100% 确定你在做什么,但听起来 combineLatest 可能有用

    // Firebase auth service observable
    let authSub = this.afAuth.authState;
    
    // Current route service observable
    let routeSub = this.activatedRoute.url;
    
    const sub = combineLatest(authSub, routeSub)
       .pipe(
         tap(([authResult, routeResult]) => {
             // the above uses array param destructuring, so we have two variables
             if(authResult === something and routeResult === something){
                //Do Something
             } 
             else {
               // do something else
             }
         })
       )
       .subscribe();
    

    activatedRoute.url 是一个observable of a BehaviorSubject,所以它会在您订阅时为您提供最新结果,我认为 Firebase 身份验证服务也会如此,但您必须对其进行测试

    这里可能有语法错误,直接输入就行了,不过是你的基本combineLatest

    【讨论】:

    • 请注意,在每个 observable 发出至少一个值之前, combineLatest 不会发出初始值。
    • activatedRoute.url 是一个 BehaviorSubject 的 observable,所以它会给你最新的结果“Once”,因此上面的代码没有进入 subscribe()。这对我来说是一个惊喜,所以我将代码更改为 combineLatest(this.afAuth.authState, this.router.events).subscribe() 并且运行良好。非常感谢。
    • 这可能会被多次命中,因为 router.events 会触发数十个事件。您可以添加pipe( filter()) 以仅处理您感兴趣的事件,例如filter(event =&gt; event instanceof NavigationEnd)。您的参数会有所不同,但想法相同
    【解决方案3】:

    最后,在@Drenai 响应的帮助下,我得到了这个代码 sn-p。

    private isLoginRequired():void {
    let authSub: Observable<firebase.User> = this.afAuth.authState;
    let routeSub: Observable<any> = this.router.events;
    
    combineLatest(authSub, routeSub).subscribe(
      ([authSubSnap, routeSubSnap]):void => {
        let islogin: boolean = !!authSubSnap;
        let current_url: string = routeSubSnap.url;
    
        if (!islogin) {
          this.router.navigate([this.loginPageURL]);
        } else if (current_url === this.loginPageURL && islogin) {
          this.router.navigate(["tool/event-management/events"]);
        }
      }
    );
    }
    

    【讨论】:

      猜你喜欢
      • 2021-02-02
      • 2019-05-31
      • 2020-09-01
      • 1970-01-01
      • 2019-06-09
      • 2018-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多