How to get last emitted Observable

2019-06-28 04:37发布

问题:

I've created an Angular service that basically sets an object with some user information. When I change the user, the service emits an observable, captured by some sibling components to update the info. The problem is that the first time I set the data, there's no one subscribed yet, so I get an undefined value. I've read about converting the observable to 'hot' using publish() or share() but I'm pretty new to RxJS and I still haven't understood how it works at 100% so I'm a little lost.

The service code (the relevant part):

getFwcUser() : Observable<FwcUser> {
  return this.subjectFwcUser.asObservable();
}

setFwcUser(user : FwcUser) {
  this.fwcUser = user;

  this.subjectFwcUser.next(this.fwcUser);
}

NOTE: FwcUser is an interface with some user fields. I run setFwcUser inside a button handler to choose the user.

The late subscriber (component code):

ngOnInit() {
  this.fwcUserService.getFwcUser()
    .subscribe(
      (user : FwcUser) => { console.log('Received: ', user); this.fwcUser = user; }
  );
  [......]
}

Always prints: 'Received: undefined'. NOTE: The component is inside an '*ngIf="fwcUser"', so it's being loaded when I call to 'setFwcUser' from a sibling component.

How could I get this component to read the lost value?

Also, could someone recommend a good resource to learn about RxJS for beginners? I'm reading a couple of books on Angular4 but none of them explain it clearly...

Thanks!

回答1:

BehaviorSubject is the answer here. Rather than a standard subject (Observable) that just emits values as they come in, a BehaviorSubject emits the last value upon subscribe(). You can also get the last value manually using the BehaviorSubjects getValue() method.

One note to make about the code below is that the null block in the BehaviorSubject declaration represents the initial value of the BehaviorSubject upon initialization. You could either leave it, or pre-populate it with a value.

Service

EDITED: Corrected a couple of typing errors.

fwcUser: BehaviorSubject<FwcUser> = new BehaviorSubject<FwcUser>(null);
fwcUser$ = this.fwcUser.asObservable();

// Set the value of the fwcUser
updateFwcUser(user) {
    this.fwcUser.next(user);
}

Component

// Subscribe to the BehaviorSubject
ngOnInit() {
    this.fwcUserService.fwcUser$.subscribe(_fwcUser => {
        this.fwcUser = _fwcUser
    }
}

// Get the current (previously emitted) value manually
getFwcUser() {
    this.fwcUser = this.fwcUserService.fwcUser.getValue();
}


回答2:

Question? How in the world do you deal with the first null value emitted? I have a behaviorSubject and when i subscribe to it i receive a null first and then the value after.

How do i get just the second value.

My Service

user: BehaviorSubject<User> = new BehaviorSubject(null); // The behavior-roles of the user.

authState: any = null; // The object of the user observed.
loginForm: any = false; // Login Popup
signupForm: any = false; // Signup Popup
customToken: any; // Custom Token Variable
provider: any; // Social Provider Type
loginButton: boolean; // Login Button Hidden Y/N
logoutButton: boolean; // Logout Button Y/N

    constructor(private afAuth: AngularFireAuth,
                private db: AngularFireDatabase,
                private router: Router) {
                // this.afAuth.authState.subscribe((auth) => {
                // this.authState = auth;
                // }); // Old Method

                this.afAuth.authState
                .switchMap(auth => {
                if (auth) {
                    /// signed in
                    const authData = this.db.list<User>('users/' + auth.uid).valueChanges();
                    console.log(authData);
                    return this.db.list<User>('users/' + auth.uid).valueChanges();
                } else {
                    /// not signed in
                    return Observable.of(null);
                }
                })
                // .subscribe(console.log); // check the subscription
                .subscribe((userData) => {
                console.log(userData);
                this.user.next(userData);
                // this.user.next(user);
                // console.log(this.user.next(userData));
                });
            }

My GuardService

return this.auth.user.map(value => {
       const checkValue = _.has(_.get(value, '2'), 'basicUser', true);
       if (checkValue === true) {
         console.log('Route was true');
         return true;
       } else if (checkValue === false) {
        console.log('Route was False');
       }});