I am new to Observable style programming. I have a question: I want to share user info across the app between component - and I use BehaviorSubject to share this info. This is inspired by sharing BehaviorSubject as AuthInfo. If I can share AuthInfo which contain a uid across my app component, why can I use it to share my user object data?
The problem is, the user object, I am getting that from Firebase. So it is an Observable, and I have no idea how can I assign this to a Behavior Subject. So here is the code I attempted:
@Injectable()
export class AuthService {
static UNKNOWN_USER = new AuthInfo(null);
static EMPTY_USER = new User(null, null, null, null);
authInfo$: BehaviorSubject<AuthInfo> = new BehaviorSubject<AuthInfo>(AuthService.UNKNOWN_USER);
userInfo$: BehaviorSubject<User> = new BehaviorSubject<User>(AuthService.EMPTY_USER);
constructor(private auth: FirebaseAuth, private db:AngularFireDatabase, @Inject(FirebaseRef) fb) {
//firebase way to see if user is login or not
this.auth.subscribe(user => {
if (user) {
const authInfo = new AuthInfo(user.uid);
this.authInfo$.next(authInfo);
//This is the part I do not know how to do
[OPTION 1]: this.userInfo$.next(findUserByuid(user.uid)) ?? - error
[OPTION 2]: this.userInfo$ = findUserByuid(user.uid) ?? - userInfo$ turn into an observerable, which when I logout to call this.userInfo$.next(AuthService.EMPTY_USER) it will throw error as .next() is not a function.
[OPTION 3]:
this.findUserByuid(user.uid).subscribe(user => {
this.userInfo$.next(user);
});
---- Can I put a subscribe inside of another subscribe to chain it like promise? I am not sure this is following the best practices. But this is the only option that there is no error. But just not sure I am doing it right. I do get the user object no problem
}
else {
this.authInfo$.next(AuthService.UNKNOWN_USER);
}
});
}
findUserByuid(uid:string):any {
//return a firebase object observerable
return this.db.object('userProfile/' + uid).map(user => User.fromJson(user));
}
logout() {
this.userInfo$.next(AuthService.EMPTY_USER);
this.authInfo$.next(AuthService.UNKNOWN_USER);
this.auth.logout();
}
And the bigger question, should I share data across application using behavior subject? Or should I just use observable like user$ = findUserByuid(uid) and make user$ as AuthService member variable. And then other components just subscribe to user$?
I am very confused on the best ways to share data across Angular2 WITHOUT using a dummy service like in the old way Angular 1. I saw ppl using Behavior Subject to share auth state - which is why I have all these questions and implementation. Any help will be greatly appreciated! There is no much resource out there regarding Behavior Subject.
[UPDATE]: The reason I choose behavior subject instead of just observable to pass data between different part of my app: Behavior Subject VS regular Observable
This is how I would write it:
...
authInfo$: Observable<AuthInfo>;
userInfo$: Observable<User>;
constructor(private auth: FirebaseAuth, private db:AngularFireDatabase, @Inject(FirebaseRef) fb) {
//firebase way to see if user is login or not
this.authInfo$ = auth
.startWith(null) // in case auth has no initial value
.map(user => user ? new AuthInfo(user.uid) : AuthService.UNKNOWN_USER)
.publishReplay(1)
.refCount();
// automatically update the unserInfo$ when authInfo$ changes (no matter from where)
// ...we are "reacting" on the data emitted by authInfo$ ... hence the name "reactive" programming
this.userInfo$ = this.authInfo$
.switchMap(authInfo => this.findUserByuid(authInfo.uid))
.publishReplay(1);
// this activates the whole stream-flow
this.userInfo$.connect(); // instead of .connect() you could also append a ".refCount()" after the ".publishReplay(1)", however then it would only be acitve when there is at least 1 subscriber, so it depends on what you want
}
findUserByuid(uid?: string): Observable<User> {
if (uid == null) {
return Observable.of(AuthService.EMPTY_USER);
} else {
return this.db.object('userProfile/' + uid)
.map(user => User.fromJson(user));
}
}
logout() {
this.auth.logout();
}
...
In my opinion the best way two share data across the application it's using the redux pattern that in angular 2 it's implement with ngrx/store plus ngrx/effects. As well as the smart compoment and presentation component pattern.
I have a example with those patterns
https://github.com/vigohe/ionic2-marvelApp
My running example:
https://enigmatic-plains-75996.herokuapp.com/
Official example from ngrx repo:
https://github.com/ngrx/example-app
More info:
Try to learn it because it will change your life ;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With