My goal is to pass an observable from a parent component to its children, I can't do it using "Input" and "Output" because the component that contains the child components is itself a component of an internal library for user interfaces.
I have a father component: home.componente.ts
In the parent component HomeComponent, I call a service from the OnInit event and store its response in a "Subject" type to store an observable so I can subscribe to child components:
this.tabs = [
{id: 'reportsTab', label: 'Files', routerLink: '/reports/1'},
{id: 'processesTab', label: 'Tasks', routerLink: '/processes/1'},
];
ngOnInit(): void {
this.processesService.getInfo(id).subscribe(response => {
this.processesService.data.next(response);
});
}
This is the processes.service.ts service:
export class ProcessesService {
data: Subject<RestResponse<ScheduleDataOutput[]>> = new Subject();
public getInfo(id: number): Observable<RestResponse<ScheduleDataOutput[]>> {
let serviceName = "/processes/info";
if (id) {
serviceName = serviceName '?id=' id;
}
return this.service.get<any>(serviceName)
.pipe(
tap(_ => this.log('fetched info')),
catchError(this.handleError<ScheduleDataOutput[]>('getInfo', []))
);
}
getData() {
return this.data.asObservable()
}
}
In the home.component.html file, we have the inner component, it's a UI component for to use the classic sistem of tabs, and each tab is a child component:
<app-menu-tab [tabs]="tabs"></app-menu-tab>
When, I click on a tab, it has a routerLink that loads the child components, to which I want to pass the response (observable) obtained in HomeComponent, subscribe to the observable in the child component and with that response paint a Datagrid in the child components.
When refreshing the screen if I correctly load the Datagrids of the childs components with the response obtained in the HomeComponent component, the problem is that when I click on the tabs the Datragrids are not loaded.
This is the OnInit event of the "reportsTab" tab component:
ngOnInit(): void {
// Here I access
this.reportsSubscription = this.processesService.getData().subscribe(response => {
// But within the subscription I do not access.
console.log("response: ", response)
});
}
The issue is that I cannot access within the subscription, at some point the observable was misconfigured, the same happens with the child component of the "processesTab" tab, when I click on the tab, I cannot access the subscription of the observable:
ngOnInit(): void {
// Here I access
this.tasksSubscription = this.processesService.getData().subscribe(response => {
// But within the subscription I do not access.
console.log("response: ", response)
});
}
What could be the problem? If I am passing the observable to the childs components, but it only works and loads the Datagrids, when I reload the page, but if I click on the tabs, I lose the response obtained in the HomeComponent and the Datagrids appear empty.
CodePudding user response:
You need to use a BehaviorSubject
(or a ReplaySubject
if you don't need an initial value), not a plain Subject
.
The BehaviorSubject
will emit the latest value to new subscribers, while Subject
won't do that.
In order for a subscriber to get the data emitted from a subject, it needs to be subscribed to it prior to the data being emitted.
In short, the data property should look like this:
data = new BehaviorSubject<RestResponse<ScheduleDataOutput[]>>(null);
Or, if the initial value is superfluous, you could go with a ReplaySubject
instead:
data = new ReplaySubject<RestResponse<ScheduleDataOutput[]>>(1);