What is the difference between the following two observable mappings?
(if something in the following code appears strange to you: it stems from a learning-by-doing hobby project; I still learn RxJS)
I have a component with a getter and a constructor. Both read information from the app's ngrx store and extract a string (name).
The only difference between the getter and the constructor: the getter is used in the HTML and the observable it returns is sent through an async pipe, whereas the observable mapping in the constructor is finished by a subscription using subscribe. I expect both of them to fire as often as a new value for name becomes available.
But instead only the getter works that way and provides the async pipe in the HTML where it is used with new values of name (console.log('A') is called for every name change). The subscribe subscription's callback is called only once: console.log('B') and console.log('B!') are both called exactly once and never again.
How can this difference in behavior be explained?
Snippet from my component:
// getter works exactly as expected:
get name$(): Observable<string> {
console.log('getter called')
return this.store
.select(this.tableName, 'columns')
.do(_ => console.log('DO (A)', _))
.filter(_ => !!_)
.map(_ => _.find(_ => _.name === this.initialName))
.filter(_ => !!_)
.map(_ => {
console.log('A', _.name)
return _.name
})
}
// code in constructor seems to lose the subscription after the subscription's first call:
constructor(
@Inject(TablesStoreInjectionToken) readonly store: Store<TablesState>
) {
setTimeout(() => {
this.store
.select(this.tableName, 'columns')
.do(_ => console.log('DO (B)', _))
.filter(_ => !!_)
.map(_ => _.find(_ => _.name === this.initialName))
.filter(_ => !!_)
.map(_ => {
console.log('B', _.name)
return _.name
})
.subscribe(_ => console.log('B!', _))
})
}
Additional information: If I add ngOnInit, this life cycle hook is called exactly once during the whole test. If I move the subscription from the constructor to the ngOnInit life cycle hook, it does not work any better than from within the constructor. Exactly the same (unexpected) behavior. The same applies to ngAfterViewInit and further life cycle hooks.
Output for the name changes 'some-name' -> 'some-other-name' -> 'some-third-name' -> 'some-fourth-name' -> 'some-fifth-name':
[UPDATE] as suggested by Pace in their comment, I added getter call logs
[UPDATE] dos added as suggested by Pace
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-name
DO (B) (3) [{…}, {…}, {…}]
B some-name
B! some-name
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-other-name
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-third-name
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-fourth-name
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-fifth-name
Example content of the output printed by the console.logs in the dos:
[
{
"name": "some-name"
},
{
"name": "some-other-name"
},
{
"name": "some-third-name"
}
]
Seems as if the subscribe subscription gets lost after its first call. But why?
doright after the select to see if the new events are getting filtered out? - PaceinitialName? - bygrace