Based on the question, it appears that there is some confusion regarding the BehaviorSubject
and its functionality.
A Subject
serves as a stream of values from a source that can be subscribed to. Whenever a new value is emitted, it is received in the subscribe
method.
If you have an initial state, utilizing a BehaviorSubject
allows you to initialize the subject and guarantee that new subscribers always receive a value.
In cases where there is no initial state but you want to ensure that new subscribers receive the last emitted value upon subscription (if one exists), then the ReplaySubject
may be used.
All values entering a Subject
are of type T, denoted by Subject<T>
. Therefore, in this scenario, everything passed into the Subject
should be an AddressDto
.
For instances where an initial address is present, setting up a BehaviorSubject
would look like the following.
// Obtain the initial address somehow.
const address = new AddressDto();
const test = new BehaviorSubject<AddressDto>(address);
// All subscribers will receive this address upon subscribing.
// Event occurs.
// Another address is obtained; emit it.
const newAddress = new AddressDto();
test.next(newAddress);
// New subscribers will receive newAddress upon subscribing.
Alternatively, if there is no initial address available, a ReplaySubject
can be employed as demonstrated below.
// Always emit the last address to new subscribers by initializing it with 1.
// Subscribers won't receive an address until one is emitted.
const test = new ReplaySubject<AddressDto>(1);
// Event occurs.
// Retrieve the first address and emit it.
const firstAddress = new AddressDto();
test.next(firstAddress);
// Existing subscribers receive firstAddress.
// Future subscribers will also receive firstAddress upon subscribing.
// Event occurs.
const secondAddress = new AddressDto();
test.next(secondAddress);
// Existing subscribers now receive secondAddress.
// Future subscribers will receive secondAddress upon subscribing.
Edit
There was a query about storing the last value in a variable, which seems unclear. Assuming it refers to the source, I'll delve into that further.
Once you grasp the concept of Subject
and Observable
, understanding the notion of the Observable
Pipe
becomes clearer. Various operations can take place within a Pipe
; envision it as a sequence of actions that can be performed on an object. It resembles a series of chained array functions in JavaScript.
One such operation in a Pipe
involves executing "side effects" using the Tap
operator. This allows intermediary actions to occur while allowing data to flow through. For example, storing the value in a variable or in localStorage
are conceivable options.
If you control what enters the Subject
, performing this within a Pipe
might seem redundant. The subsequent example illustrates how to cache the outcome of an HTTP request.
this.http.get(url).pipe(
// Convert the HTTP response into a specified object.
map(response => this.mapResponseToMyClass(response)),
// Retain the mapped object for future use.
tap(myClass => {
// Execute any desired 'side effect' actions, such as below.
console.log(myClass);
// Store the value in a variable.
this.cachedMyClass = myClass;
})
);
'Piping' your own Subject
follows a similar approach. Any input to a Subject
traverses through a Pipe
before reaching the subscriber.
private subject = new Subject<AddressDto>();
getPostcode(): Observable<string> {
// Reuse the local Subject. Subscribers to this function will obtain addresses passing through the Pipe.
return subject.pipe(
map(address => address.postcode),
// Retain the last postcode in a local property.
tap(postcode => this.lastPostcode = postcode)
// The postcode is then issued to all subscribers here
).asObservable();
}