在填充之前订阅可观察对象

Subscribing to an observable before it's been populated

我正在尝试为 Angular 4 项目创建一个用户配置文件服务,并且在如何正确初始化和更新可观察的配置文件对象方面遇到了一些困难。目前,当用户进行身份验证(通过 Firebase)时,AuthService 会通过后者的 initialize() 函数将用户的身份验证信息传递给 UserProfileService。 UserProfileService 然后查找用户的个人资料(或者如果 none 存在则创建一个)并使用该个人资料填充一个 public 可观察对象。

我 运行 遇到的问题是应用程序的其他部分在这一切发生之前尝试订阅可观察到的配置文件。我最初是通过 ...

初始化 observable
public profileObservable: UserProfile = null;

... 这当然会导致 "subscribe() does not exist on null" 错误,所以我将其更改为 ...

public profileObservable: Observable<UserProfile> = Observable.of();

这至少不会引发任何错误,但是在我将 Firebase 对象映射到它之前订阅 profileObservable 的任何内容都不会更新。

下面是用户 profile.service.ts 的完整代码。我仍在努力弄清楚其中的一些是如何工作的,所以希望有人能阐明一些问题。谢谢!

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { FirebaseListObservable, FirebaseObjectObservable, AngularFireDatabase } from 'angularfire2/database';
import * as firebase from 'firebase/app';

export class UserProfile {
    $exists: Function;
    display_name: string;
    created_at: Date;
}

@Injectable()
export class UserProfileService {
    private basePath: string = '/user-profiles';
    private profileRef: FirebaseObjectObservable<UserProfile>;
    public profileObservable: Observable<UserProfile> = Observable.of();

    constructor(private db: AngularFireDatabase) {
        // This subscription will never return anything
        this.profileObservable.subscribe(x => console.log(x));
    }

    initialize(auth) {
        this.profileRef = this.db.object(`${this.basePath}/${auth.uid}`);
        const subscription = this.profileRef.subscribe(profile => {
            if (!profile.$exists()) {
                this.profileRef.update({
                    display_name: auth.displayName || auth.email,
                    created_at: new Date().toString(),
                });
            } else subscription.unsubscribe();
        });

        this.profileObservable = this.profileRef.map(profile => profile);

        // This subscription will return the profile once it's retrieved (and any updates)
        this.profileObservable.subscribe(profile => console.log(profile));
    }
};

一旦构造了可观察引用,就不得更改它们。我发现将订阅者与数据源正确分离的方法是使用中间体 Subject,它既是观察者又是可观察者。

您的代码将如下所示:

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

...

export class UserProfileService {
    ...
    public profileObservable = new Subject<UserProfile>(); 

    constructor(private db: AngularFireDatabase) {
        // This subscription now works
        this.profileObservable.subscribe(x => console.log(x));
    }

    initialize(auth) {
        const profileRef = this.db.object(`${this.basePath}/${auth.uid}`);

        ...

        profileRef.subscribe(this.profileObservable);
    }
};