I\'m looking into Angular RxJs patterns and I don\'t understand the difference between a BehaviorSubject
and an Observable
.
From my underst
One thing I don't see in examples is that when you cast BehaviorSubject to Observable via asObservable, it inherits behaviour of returning last value on subscription.
It's the tricky bit, as often libraries will expose fields as observable (i.e. params in ActivatedRoute in Angular2), but may use Subject or BehaviorSubject behind the scenes. What they use would affect behaviour of subscribing.
See here http://jsbin.com/ziquxapubo/edit?html,js,console
let A = new Rx.Subject();
let B = new Rx.BehaviorSubject(0);
A.next(1);
B.next(1);
A.asObservable().subscribe(n => console.log('A', n));
B.asObservable().subscribe(n => console.log('B', n));
A.next(2);
B.next(2);
BehaviorSubject vs Observable : RxJS has observers and observables, Rxjs offers a multiple classes to use with data streams, and one of them is a BehaviorSubject.
Observables : Observables are lazy collections of multiple values over time.
BehaviorSubject:A Subject that requires an initial value and emits its current value to new subscribers.
// RxJS v6+
import { BehaviorSubject } from 'rxjs';
const subject = new BehaviorSubject(123);
//two new subscribers will get initial value => output: 123, 123
subject.subscribe(console.log);
subject.subscribe(console.log);
//two subscribers will get new value => output: 456, 456
subject.next(456);
//new subscriber will get latest value (456) => output: 456
subject.subscribe(console.log);
//all three subscribers will get new value => output: 789, 789, 789
subject.next(789);
// output: 123, 123, 456, 456, 456, 789, 789, 789
The Observable object represents a push based collection.
The Observer and Observable interfaces provide a generalized mechanism for push-based notification, also known as the observer design pattern. The Observable object represents the object that sends notifications (the provider); the Observer object represents the class that receives them (the observer).
The Subject class inherits both Observable and Observer, in the sense that it is both an observer and an observable. You can use a subject to subscribe all the observers, and then subscribe the subject to a backend data source
var subject = new Rx.Subject();
var subscription = subject.subscribe(
function (x) { console.log('onNext: ' + x); },
function (e) { console.log('onError: ' + e.message); },
function () { console.log('onCompleted'); });
subject.onNext(1);
// => onNext: 1
subject.onNext(2);
// => onNext: 2
subject.onCompleted();
// => onCompleted
subscription.dispose();
More on https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md
An observable allows you to subscribe only whereas a subject allows you to both publish and subscribe.
So a subject allows your services to be used as both a publisher and a subscriber.
As of now, I'm not so good at Observable
so I'll share only an example of Subject
.
Let's understand better with an Angular CLI example. Run the below commands:
npm install -g @angular/cli
ng new angular2-subject
cd angular2-subject
ng serve
Replace the content of app.component.html
with:
<div *ngIf="message">
{{message}}
</div>
<app-home>
</app-home>
Run the command ng g c components/home
to generate the home component. Replace the content of home.component.html
with:
<input type="text" placeholder="Enter message" #message>
<button type="button" (click)="setMessage(message)" >Send message</button>
#message
is the local variable here. Add a property message: string;
to the app.component.ts
's class.
Run this command ng g s service/message
. This will generate a service at src\app\service\message.service.ts
. Provide this service to the app.
Import Subject
into MessageService
. Add a subject too. The final code shall look like this:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class MessageService {
public message = new Subject<string>();
setMessage(value: string) {
this.message.next(value); //it is publishing this value to all the subscribers that have already subscribed to this message
}
}
Now, inject this service in home.component.ts
and pass an instance of it to the constructor. Do this for app.component.ts
too. Use this service instance for passing the value of #message
to the service function setMessage
:
import { Component } from '@angular/core';
import { MessageService } from '../../service/message.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent {
constructor(public messageService:MessageService) { }
setMessage(event) {
console.log(event.value);
this.messageService.setMessage(event.value);
}
}
Inside app.component.ts
, subscribe and unsubscribe (to prevent memory leaks) to the Subject
:
import { Component, OnDestroy } from '@angular/core';
import { MessageService } from './service/message.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
message: string;
subscription: Subscription;
constructor(public messageService: MessageService) { }
ngOnInit() {
this.subscription = this.messageService.message.subscribe(
(message) => {
this.message = message;
}
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
That's it.
Now, any value entered inside #message
of home.component.html
shall be printed to {{message}}
inside app.component.html