I am learning RxJs, I am seeking confirmation or correction on my assumption.
I am trying to make a public read only observable in a service that I can use .next() on in various places in my service class. I am wondering if this is the proper way to do it:
private myObservable = new Subject<T>();
public myObservable$: Observable<T> = this.myObservable.asObservable();
The user can subscribe to myObservable$
I can usemyObservable.next(...);
It works perfectly but I am experience enough to know that I may just be being an unwitting idiot (RxJS is huge). Is this correct pattern and correct object for said use case?
What you're doing is correct. There's however still a little shorter notation. Since Subject is already an Observable (it inherits the Observable class) you can leave the type checking to TypeScript:
private myObservable = new Subject<T>();
public myObservable$: Observable<T> = this.myObservable;
Any consumer of your service can subscribe to myObservable$ but won't be able to call myObservable$.next() because TypeScript won't let you do that (Observable class doesn't have any next() method).
This is actually the recommended way of doing it and RxJS internally never uses asObservable anyway. For more detailed discussion see:
https://github.com/ReactiveX/rxjs/pull/2408
https://github.com/ReactiveX/rxjs/issues/2391
See a very similar question: Should rxjs subjects be public in the class?
In project we are using this kind of Observables, this is giving you proper encapsulation to your private observable, but you still can call next() using some public method.
private sourceName = new Subject<T>();
name = this.sourceProductName.asObservable();
sendName(item: T) {
this.sourceName.next(item);
}
Related
What is the difference between subscribing to subject and subscribing to asObservable?
What is the difference between the following?
this.subject.subscribe((data) => this.datas.push(data));
this.subject.asObservable().subscribe((data) => this.datas.push(data));
Seems like both are same!
From the subscription side, there is no difference; emissions are received exactly the same.
asObservable() is used to hide the Observer behavior from consumers, preventing them from nexting values into the subject.
In angular, you'll see this in a lot services where you want consumers of the service to have access to the emitted values, but you don't want them to be able to call .next on the subject:
class SomeService {
private subject$ = new Subject();
public observable$ = this.subject$.asObservable();
}
I've got a service that I use to share data between 2 components. That part works flawlessly, but now I need to call a method of component A, when something triggers on the service (and pass a value to that component). How can I do this? I read on older questions that this is a wrong approach but since Im a noob I dont know what to search for a solution.
Do I need to use observables?
I think Joseph's idea is the way to go.
Here's how I'd implement it:
class FooService {
private _newEvents = new Subject();
newEvents$ = this._newEvents.asObservable();
addNewEvent (ev) {
this._newEvents.next(e);
}
}
// Allow `A` class to communicate with `B` class
class A {
addEvent (ev) {
this.fooService.addNewEvent(ev);
}
}
class B {
private subscription: Subscription;
ngOnInit () {
this.subscription = this.fooService.newEvents$
.subscribe(e => {})
}
ngOnDestroy () {
this.subscription.unsubscribe();
}
}
Note that if your B class subscribes to multiple observables, you should unsubscribe from them using, among other solutions, takeUntil.
Observables / Subjects are one way. You would have one Subject in the service, and would use .next(value) on it to exchange values. Each component which is interested in the value may subscribe to that subject.
Example: (taken from RxJS docs
//your Service
import { Subject } from 'rxjs';
const subject = new Subject<number>();
//Component A (and others as well)
service.subject.subscribe({
next: (num) => console.log(num)
});
//this should work as well with prettier syntax:
service.subject.subscribe(sum =>
console.log(num)
);
//Component B
service.subject.next(7) //passing number 7 to Component A
Whenever you create a subscription, make sure to always unsubscribe! Else you might end up with stacks of subscriptions, which will all get triggered simultaneously in the very same component.
From personal experience, I found it more helpful to outsource any functions and variables that could be considered as global into a dedicated service, if possible. If you directly read the variables of a service from your components (and modify them if necessary), you'll have the same effect. That works as long as you keep a proper service structure. Some examples of dedicated services with global use are:
Translations (TranslationService)
Rights Management (PermissionService)
I have learned of two different ways of making an Observable. First one was with a Subject, like so:
// file A
const message$ = new Subject();
// file B
message$.subscribe( (message) => console.log(message) );
// file C
message$.next("Hello there!");
This method of creating an Observable allows me to have a way to exchange data from file B to file C.
The second way is via the Observable class, like so:
// file A
const click$ = new Observable( function(observer) {
//Alternatively, I can use Observable.create()
document.addEventListener('click', (e) => observer.next(e));
});
// file B
click$.subscribe( (cl) => console.log(cl) );
The main difference that I can gather between the Subject way and the Observable way is that I am not sure how to have some sort of communication between some file C, to the subscribers of the Observable. Basically, click$ does not have a .next() method, and the observer methods are in the function that we pass to the observable.
Other than this difference in behavior, is there another difference between observables made with Subject, and those made with Observable
A Subject is both Observable and Observer at the same time. That makes it so tempting to use, because you get a reference to an Observer that you can pass around in your code and emit items from wherever you want. However, that increases the error-proneness of your code by a lot, as you switch from a declarative definition of your Observable to a imperative one.
Generally speaking, you should use Observable creation functions (of, from, create) wherever possible. I'd say most cases can be solved without Subjects. There is a steep learning-curve though, as you must get to know most of the Observable creation functions in order to follow that pattern.
Subject might come more natural to developers who are used to code imperatively (that is: with a script language like JS), as it kind of resembles a simple wrapper object for a callback function. And one might ask, why is there a Subject anyway if its not desirable.
According to this article, Subjects are to be used in one case only:
To generate a hot observable imperatively and statefully, without any direct external source.
In short, that means: Use Subject when you don't have any external source (like an Observable, Promise or Event) and need to multicast the state of a class from inside a function. You shouldn't expose that Subject to others, though!
I suggest reading this article to you, it will clear up things.
Subject implements both the Observable and the Observer interface.
Implementing the Observable interface means, among other things, that you can subscribe to a Subject.
Implementing the Observer interface means, among other things, that with a Subject you can call the methods next error and complete.
You create Observables using Subject in the case you want to control programmatically the flow of events of that Observable, which is the case you mentioned of managing communication between C and B.
The relation between Subject and Observable goes like this:
class Observable {}
class Subject extends Observable {}
Subject implements Subscriber interface. So you can use Subject as a Subscriber (Observable cannot be used like this):
const subj = new Subject()
observable.subscribe(subj)
Subject can hide it's Subscriber interface by calling asObservable method - turning Subject into plain Observable;
I generally use Subject when my code is going to generate events and I use Observable when I already have some source of events
For example using Subject as a queue:
const queue = new Subject();
queue.pipe(
concatMap(doStuff)
).subscribe()
queue.next('do this')
queue.next('do that')
or using Subject as a signaller to stop other Observable
const stop = new Subject();
someObservable.pipe(
map(x => x + 1)
takeUntil(stop)
)
// somewhere in my code
stop.next()
Other than this difference in behavior, is there another difference between observables made with Subject, and those made with Observable
I wouldn't say there are differences - It is more like Subject is a complement to plain Observable - allowing us to do more things when needed.
I found the rxJS docs define them as
What is a Subject? An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers. While plain Observables are unicast (each subscribed Observer owns an independent execution of the Observable), Subjects are multicast.
and it goes on to give examples but I'm looking for a basic ELI5 explanation. From my understanding is it helps handle and define items in a sequence. Is that correct?
I think it would be most helpful to me and others to see a simple function with and without defining an rxJS Subject to understand why it's important?
Thanks!
The easiest way to understand it is to think of a Subject as both a producer and a consumer. It's like an open channel where someone can send a message on one end, and any subscribers will receive it on the other end.
+---------------
Sender | => => => => Subscriber
-----------------------+ +-----------
Message => => => => => => => => => => => Subscriber
-----------------------+ +-----------
| => => => => Subscriber
+---------------
In code terms say you have a service with a subject
class MessageService {
private _messages = new Subject<Message>();
get messages: Observable<Message> {
return this._messages.asObservable();
}
sendMessage(message: Message) {
this._messages.next(message);
}
}
Note the messages getter returning the Subject as an Observable. This is not required. The Subject is already an observable, and anybody could subscribe directly to the Subject. But I think the asObservable pattern is used as a way to limit what users can do with it, i.e. so users only use it to subscribe to, and not emit to. We save the emitting for the sendMessage method.
Now with this service in place, we can inject it into different components, and this can be a way for two (or more) arbitrary components to communicate (or just receive arbitrary event notifications).
class ComponentOne {
constructor(private messages: MessageService) {}
onClick() {
this.messages.sendMessage(new Message(..));
}
}
class ComponentTwo {
constructor(private messages: MessageService) {}
ngOnInit() {
this.messages.messages.subscribe((message: Message) => {
this.message = message;
});
}
}
Angular's own EventEmitter is actually a Subject. When we subscribe to the EventEmitter, we are subscribing to a Subject, and when we emit on the EventEmitter, we are sending a message through the Subject for all subscribers.
See also:
Subject vs BehaviorSubject vs ReplaySubject in Angular
Subjects are useful when the code you're in is the one that is actually originating the observable data. You can easily let your consumers subscribe to the Subject and then call the next() function to push data into the pipeline.
If, however, you are getting data from other source and are just passing it along (perhaps transforming it first), then you most likely want to use one of the creation operators shown here, such as Rx.Observable.fromEvent like so:
var clicks = Rx.Observable.fromEvent(document, 'click');
clicks.subscribe(x => console.log(x));
This allow you to stay in the functional paradigm, whereas using a Subject, while it has its uses, is considered by some to be a smell that you're trying to force imperative code into a declarative framework.
Here is a great answer that explains the difference in the two paradigms.
If you want the most simple explanation ...
Observables are usually the result of something. The result of an http call, and whatever you do with a pipe returns an observable.
But what is the source of those things? Ever wondered how you hook your user events into the whole rxjs thing? The main feature of subjects is that you can call the next() method on them.
When doing reactive programming, the first step is usually to make a list of possible subject you will have.
For instance: lets say we have to make a todo-list app.
We will probably have a couple of variables in our component:
public deleteItem$ = Subject<TodoItem> = new Subject();
public addItem$ = Subject<TodoItem> = new Subject();
public saveList$ = Subject<TodoItem[]> = new Subject();
and in our applicatiuon we will hook these up like this:
<button (click)="deleteItem$.next(item)">Delete</button>
Using rxjs, we will use operators like merge/combineLatest/withLatestFrom to handle these subjects and define our application logic.
I'll see if I can find the time to make a small example.
You can find a study of the semantics of subjects here.
All answered I see are correct. I'll just add that the term subject comes from the observer pattern (cf. https://en.wikipedia.org/wiki/Observer_pattern). As such a subject is sort of a relay, it receives something on one end, and emit it on any of its ends (subscriptions).
I'm creating a class for my app that keeps an internal BehaviorSubject. Is there a way I can expose a corresponding 'BehaviorObservable', which pipes through the values of the subject, but only has the 'read-only' stuff of the Observable interface? The .next(), .error() and .complete() methods should only be available internally.
It's not just a matter (I believe) of using Observable.create() to pipe the subject's values through. Users of my API should .subscribe() to the exposed observable, and then immediately get a callback for the stored current value.
I may be able to hack something together, but I'm sure I'm just missing something that RxJS can already do.
Are you by any chance looking for the .asObservable() method - which existed in Rxjs v4. Don't know if that is still the case in Rxjs v5 though.
According to this, the method should have been included in release 5.0.0-beta.2. Also I quote here their work-around if the functionality is not there :
You can get the same functionality by creating an observable with the private subject's subscribe function:
> const subj = new rx.Subject();
> const exposed = new rx.Observable(fn => subj.subscribe(fn));
For more details about subjects' semantics, you can have a look here.