I've started learning Angular, and I run into this example from official docs regarding parent-service communication using a service:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class MissionService {
// Observable string sources
private missionAnnouncedSource = new Subject<string>();
private missionConfirmedSource = new Subject<string>();
// Observable string streams
missionAnnounced$ = this.missionAnnouncedSource.asObservable();
missionConfirmed$ = this.missionConfirmedSource.asObservable();
// Service message commands
announceMission(mission: string) {
this.missionAnnouncedSource.next(mission);
}
confirmMission(astronaut: string) {
this.missionConfirmedSource.next(astronaut);
}
}
I'm a bit confused by this part:
// Observable string streams
missionAnnounced$ = this.missionAnnouncedSource.asObservable();
missionConfirmed$ = this.missionConfirmedSource.asObservable();
I understand that .asObservable()
creates a new Observable with this Subject as the source. However, why do we need it ? missionAnnouncedSource
is already an observable.
Why don't we just simply have a service like this:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class MissionService {
// Observable string sources
missionAnnouncedSource = new Subject<string>();
missionConfirmedSource = new Subject<string>();
// Service message commands
announceMission(mission: string) {
this.missionAnnouncedSource.next(mission);
}
confirmMission(astronaut: string) {
this.missionConfirmedSource.next(astronaut);
}
}
Of course, I would need to adjust code in components to make it work, but I do not see any issues with that:
//astronaut.component.ts
constructor(private missionService: MissionService) {
this.subscription = missionService.missionAnnouncedSource.subscribe(
mission => {
this.mission = mission;
this.announced = true;
this.confirmed = false;
});
}
Is this some sort of convention or I am missing something?
CodePudding user response:
You don’t need it, but the idea is that if you only expose an observable, it is not possible to next
the public variable - it should only be done from within the service.
CodePudding user response:
Your question give me the same doubt. So I look in to it. This is what I have found:
From de RxJS docs
A Subject is like an Observable, but can multicast to many Observers. Subjects are like EventEmitters: they maintain a registry of many listeners.
So you can do the same just that Observable cant modify the value with a next(value)
call.
This article is quite good, explains your doubt about best practice and this example is from there:
In the RxJS world it's considered best practice to only expose Subjects to the parts of your application that would add new data into the Observable sequence. This is the same idea behind only allowing write access to certain parts of your application, private and public class members, etc.
To create an Observable from a Subject, you can simply invoke asObservable on any Subject:
let currentUserSubject$ = new BehaviorSubject() < string > 'Eric';
let currentUser$ = currentUserSubject$.asObservable();
We now have a new variable called currentUser$ that is an Observable of currentUserSubject$'s observable sequence. To see how this works, lets subscribe to the currentUser observable and then add some data to the currentUserSubject$:
let currentUserSubject$ = new BehaviorSubject() < string > 'Eric';
let currentUser$ = currentUserSubject$.asObservable();
currentUserSubject$.subscribe(val => {
console.log(val);
});
// => 'Eric'
currentUserSubject$.next('hello');
// => 'hello'
Note that if you try and call currentUser$.next(), it will throw an error because Observables can only observe a sequence - thus providing you with read-only access to the currentUserSubject!