Home > Enterprise >  Angular 11 - How to pass data observable using a service
Angular 11 - How to pass data observable using a service

Time:06-01

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);
  • Related