I have a component that prints an observable value (test$) to the template via the async pipe.
The component property needs to be initialised based on the component's inputs, so I am assigning its value to an observable emitted by a service (test$) in ngOnInit. The observable exposed by the service is assigned to a combination of subjects when the service is initialised. The value is not printed in the template. Stackblitz
If I define the combined subjects as BehaviorSubject, the template is notified of the new value.
I presume this has something to do with cold/hot observables. It is my understanding that if you subscribe to a BehaviorSubject you will always get the latest value even if you subscribed after it emitted a value, but with cold observables (as Subject) you need to subscribe before the value is emitted in order to be notified.
So why is the template not being updated if the subscription takes place before the subjects emit a value? My reasoning is that the subscription takes place when the template has been rendered, which is in ngOnInit. The subjects don't emit their values until after this step.
Component
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
@Input() initialValue: number;
result$: Observable<number>;
constructor(private service: TestService) {
}
ngOnInit() {
console.log('component init');
this.result$ = this.service.result$;
// Get data based on inputs
this.service.initTransformedValue(this.initialValue);
}
}
Service
@Injectable()
export class TestService {
result$: Observable<number>;
otherValue$: Observable<number>;
transformedValue$: Observable<number>;
constructor() {
console.log('service constructor');
this.init();
}
init(){
this.result$ = combineLatest(
this.transformedValue$,
this.otherValue$
).pipe(map(([first, second]) => {
console.log('have combined value');
return first + second;
})
);
}
initTransformedValue(initialValue) {
// Use timeout to simulate HTTP calls
setTimeout(() => {
console.log('service. emit transformedValue value');
this.transformedValue$ = of(initialValue * 2);
}, 1000);
setTimeout(() => {
console.log('service. emit otherValue value');
this.otherValue$ = of(initialValue * 4);
}, 1200);
}
}
Template
<p>{{result$ | async}}</p>