【问题标题】:RxJS Observable & Observer issuesRxJS Observable 和 Observer 问题
【发布时间】:2016-08-15 19:37:51
【问题描述】:

我有一个 Angular 2 服务,它执行几个步骤来验证和登录应用用户。每当我尝试在我的观察者上调用 next() 时,我都会收到一个未定义的错误。当 Observable 被实例化时,我唯一可以成功调用 next() 的地方是在构造函数内部。

如果我调用 authenticateUser(),我会收到 this.isLoggedIn 未定义的错误。

AuthService.ts

public isLoggedIn$: Observable<boolean>;
private isLoggedIn: Observer<boolean>;

constructor(...) {

    this.isLoggedIn$ = new Observable<boolean>(
        (observer: Observer<boolean>) => {
            this.isLoggedIn = observer;

            // this works fine
            this.doLogin();

        }).share()

}

private doLogin = ():void => {

    let context:AuthContextModel = this.authContextService.getAuthContext();

    if (context) {

        let isAuthenticated = this.isAuthenticated(context);

        if (isAuthenticated) {

            this.doCreateCurrentUserContext(context)
                .then((result) => {return this.doNotifyLoggedInStatus(result);});
        }
    }
};


private doNotifyLoggedInStatus = (result:boolean):Promise<boolean> => {

    this.isLoggedIn.next(result);

    return new Promise((resolve, reject) => {
        return resolve(true);
    });
};


public authenticateUser = (user: string, pass: string):Promise<boolean> => {
    return this.doFetchToken(user, pass)
        .then((fetchTokenData) => {return this.doStoreToken(fetchTokenData);})
        .then((authContext) => {return this.doCreateCurrentUserContext(authContext);})
        .then((result) => {return this.doNotifyLoggedInStatus(result);});

};

【问题讨论】:

  • 我猜那是因为isLoggedIn$ 没有被订阅。
  • @estus 谢谢.. 就是这样!我已经敲了几个小时的头了..谢谢!

标签: angular rxjs rxjs5


【解决方案1】:

如果你想在 Observable 实例化之外的 Observer 上调用 .next(),你应该使用 Subject 而不是单独的 Observables 和 Observers。受试者两者兼而有之。您可以在将事件传递给它的位置单独订阅它们。

有不同种类的主题,但对于您的情况,我会推荐一个 BehaviorSubject。每当您订阅它时,它不仅会捕获未来的事件,还会返回在订阅发生之前触发的 last 事件。这对于身份验证组件很有用,因为您不必为成功登录编写单独的代码并实际检查用户是否在已经发生(或稍后注销)后通过身份验证。这也很简单。

import { BehaviorSubject } from 'rxjs/Rx'; 

//...
  public isLoggedIn: BehaviorSubject<boolean> = BehaviorSubject.create();

  constructor(...) { }

  private doLogin():void {

    //... Do the log in
    this.isLoggedIn.next(true);

  };

无论您想检查登录状态,您需要做的就是获取 BehaviorSubject 并订阅它。订阅是在调用 doLogin() 之前还是之后发生都没有关系。

如果您打算在刷新时保留登录状态,您只需在构造函数中检查身份验证并照常调用 BehaviorSubject 上的.next(true)

另见:

ReactiveX Subject docs

Intro to Rx BehaviorSubjects

【讨论】:

  • 谢谢,这是一个巨大的帮助。还有一个问题.. 让观察值影响 DOM 的诀窍是什么? *ngIf="isLoggedIn"
  • *ngIf="isLoggedIn | async" 应该工作。异步管道是必要的,因为它从 Observable(行为主体)中解包值。否则,您只是给它一个主题而不指定如何处理它。 Angular 不会喜欢这样的。如果您想使用它来影响登录组件之外的 DOM,则必须检索 isLoggedIn BehaviorSubject,因此它确实应该位于某种身份验证服务中。如果这不起作用,只需在您的组件上手动订阅它,将值分配给布尔属性并将 *ngIf 绑定到该属性。
  • 我有一个 CurrentUser 组件,它只在我的布局标题中显示“以 foo123 身份登录”。这只会在我登录后刷新应用程序时更新。我的 CurrentUser 组件获得了 AuthService 注入,并且我在构造函数中订阅了 isLoggedIn 行为主题。我还有一个 LoginComponent 注入 AuthService 并且登录表单调用 AuthService.authenticateUser() 如果一切顺利,它将在行为主题上调用 next(true)。但是这个 impl 仅在我登录后重新加载应用程序时才有效
  • 你能把 CurrentUser 组件、它的父组件、LoginComponent、它们的模板和 AuthService 放在一个 plunker 或 fiddle 中并发送给我吗?不需要编译也不需要工作,看代码就行了。
  • 今晚我会把它贴出来......但是我该如何调试这些东西呢?我认为我的 CurrentUser 构造函数(我订阅 AuthService.isloggedIn)在我的身份验证服务构造函数之前运行,这是 Observable 被实例化的位置。
猜你喜欢
  • 2016-07-06
  • 2018-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 1970-01-01
相关资源
最近更新 更多