Reputation: 115
I got several components subscribing to my data service and they are all working fine. But in one of my components, I try to subscribe twice (inside ngOnInit and ngAfterViewInit) but this doesn't work. Here is the component:
ngOnInit() {
this.dataService.data$.pipe(first()).subscribe(subscribeToData => {
this.title = this.dataService.getData("...");
this.anotherService.getData
.subscribe(another => {
this.data = data;
},
...
});
}
ngAfterViewInit() {
this.dataService.data$.pipe(first()).subscribe(subscribeToData => {
let options = {
data: {
}
...
{
title: this.dataService.getData("...");
},
...
};
...
});
}
If I remove subscribe from ngOnInit then ngAfterViewInit works fine, else it fails. So is there a way to subscribe two or more times from within the same component at the same time?
Here is the data service:
private dataSource = new ReplaySubject(1);
data$ = this.dataSource.asObservable();
loadData(... : void) {
if (sessionStorage["data"] == null {
this.http.request(...)
.map((response: Response) => response.json()).subscribe(data => {
...
sessionStorage.setItem("data", JSON.stringify(this.data));
this.dataSource.next(this.data);
...
});
} else {
this.dataSource.next(this.data);
}
}
getData(... : string){
...
}
Upvotes: 1
Views: 6655
Reputation: 1103
There are no problems with double subscribe in your code - probably you are facing some asynchronous code issues. The browser is fast enough to quickly go through component's lifecycle and to quickly call ngOnInit
and ngAfterViewInit
. Both of them will be executed almost simultaneously and lightning fast - definitely faster than a http call. In this case, in your ngOnInit
's subscribe you have another call that might be executed after the ngAfterViewInit
(although I'm not sure).
Here is an example that shows that double subscribe in a single component works: https://stackblitz.com/edit/double-subscribe?file=src/app/app.component.ts
Try to refactor your logic to be more consecutive: if your ngAfterViewInit
must be executed after all the asynchronous code in ngOnInit
is done - store the result of the ngOnInit
's chain somewhere in a variable; if your ngAfterViewInit
does not care about the ngOnInit
try to avoid accessing the same variables, especially this.data
.
Also try to avoid nested subscribe
s - they can be replaced with switchMap
/flatMap
:
ngOnInit() {
this.dataService.data$.pipe(
first(),
tap(data => this.title = this.dataService.getData(data)), // note that this should be synchronous
switchMap(data => {
// another asynchronous call here
return this.anotherService.getData(data)
})
).subscribe(finalData => {
this.data = finalData
}
To refactor your ngAfterViewInit
to be executed after ngOnInit
do the following:
onInitData$: Observable<any>;
ngOnInit() {
this.onInitData$ = this.dataService.data$.pipe(
first(),
tap(data => this.title = this.dataService.getData(data)), // note that this should be synchronous
switchMap(data => {
// another asynchronous call here
return this.anotherService.getData(data)
}),
shareReplay(1) // shareReplay(1) is important to avoid doing double http requests per subscribe
);
this.onInitData$.subscribe(data => console.log('data from ngOnInit', data));
}
ngAfterViewInit() {
this.onInitData$.pipe(switchMap(thatData) => {
// will be executed only AFTER the ngOnInit is done
return this.dataService.data$.pipe(first()).subscribe(subscribeToData => {
let options = {
data: {
}
...
{
title: this.dataService.getData("...");
},
...
};
...
});
}).subscribe(dataFromAfterViewInit => {})
}
In general, you'd want to think why do you even need ngAfterViewInit
? What do you want to achieve by splitting those calls between onInit
/afterViewInit
? Why do they access the same data in a component?
Upvotes: 1