2

I'm at a loose end here and trying to understand the flow of how angular subscriptions work.

I make a call to an API and in the response I set the data in a behaviourSubject. So I can then subscribe to that data in my application.

Normally I would use async pipes in my templates cause its cleaner and it gets rid of all the subscription data for me.

All methods are apart of the same class method.

my first try.....

exportedData: BehaviourSubject = new BehaviourSubject([]);

exportApiCall(id) {

 this.loadingSubject.next(true)
 this.api.getReport(id).pipe(
  catchError((err, caught) => this.errorHandler.errorHandler(err, caught)),
  finalize(() => => this.loadingSubject.next(false))
 ).subscribe(res => {
   this.exportedData.next(res)
 }) 
}


export(collection) {
  let x = []

  this.exportCollection(collection.id); /// calls api


  this.exportedData.subscribe(exportData => {

    if(exportData){
      x = exportData 
    }
  })

}

console.log(x)//// first time it's empthy, then it's populated with the last click of data

 /// in the template

<button (click)="export(data)">Export</button> 

My problem is....

There is a list of buttons with different ID's. Each ID goes to the API and gives back certain Data. When I click, the console log firstly gives a blank array. Then there after I get the previous(the one I originally clicked) set of data.

I'm obviously not understanding subscriptions, pipes and behavior Subjects correctly. I understand Im getting a blank array because I'm setting the behaviour subject as a blank array.

my other try

 export(collection) {
  let x = []

  this.exportCollection(collection.id).pip(tap(res => x = res)).subscribe()
  

  console.log(x) //// get blank array
 }


    exportApiCall(id) {

     return this.api.getReport(id).pipe(
      catchError((err, caught) => this.errorHandler.errorHandler(err, caught))
     ) 
   }
ronoc4
  • 625
  • 2
  • 6
  • 21
  • I don't get where is exactly your `console.log(x)` in the first example? It's somewhere outside of class methods? Also, your button has `(click)="export(data)"` but there is no such method given. – Gynteniuxas Mar 07 '21 at 11:25
  • Method updated. The console. You can see in the method. The res is assign to x. I then console x outside the method cause I want to get a hold of the data – ronoc4 Mar 07 '21 at 13:14

3 Answers3

1

Not sure about the first example - the placement of console.log() and what does the method (that is assigned on button click) do - but for the second example, you're getting an empty array because your observable has a delay and TypeScript doesn't wait for its execution to be completed.

You will most likely see that you will always receive your previous result in your console.log() (after updating response from API).

To get the initial results, you can update to such:

public exportReport(collection): void {
  this.exportCollection(collection.id).pipe(take(1)).subscribe(res => {
    const x: any = res;
    console.log(x);
  });
}

This will print your current iteration/values. You also forgot to end listening for subscription (either by unsubscribing or performing operators such as take()). Without ending listening, you might get unexpected results later on or the application could be heavily loaded.

Gynteniuxas
  • 7,035
  • 18
  • 38
  • 54
0

Make sure the following step.

  1. better to add console.log inside your functions and check whether values are coming or not.
  2. Open your chrome browser network tab and see service endpoint is get hitting or not.
  3. check any response coming from endpoints.

if it is still not identifiable then use below one to check whether you are getting a response or not

public exportReport(collection): void { 
  this.http.get(url+"/"+collection.id).subscribe(res=> {console.log(res)});
}
Raheem Mohamed
  • 789
  • 3
  • 6
  • 15
0
  1. You would use BehaviourSubject, if there needs to be an initial/default value. If not, you can replace it by a Subject. This is why the initial value is empty array as BehaviourSubject gets called once by default. But if you use subject, it wont get called before the api call and you wont get the initial empty array.

    exportedData: BehaviourSubject = new BehaviourSubject([]);

Also, you might not need to subscribe here, instead directly return it and by doing so you could avoid using the above subject.

exportApiCall(id) {
 this.loadingSubject.next(true);

 return this.api.getReport(id).pipe(
  catchError((err, caught) => this.errorHandler.errorHandler(err, caught)),
  finalize(() => => this.loadingSubject.next(false))
 );
}
  1. Console.log(x) needs to be inside the subscription, as subscribe is asynchronous and we dont knw when it might get complete. And since you need this data, you might want to declare in global score.
export(collection) {
  // call api
  this.exportApiCall(collection.id).subscribe(exportData => {
    if (exportData) {
      this.x = exportData; // or maybe this.x.push(exportData) ?
      console.log(this.x);
    }
  });
}
Gowtham Raj J
  • 937
  • 9
  • 26
  • Thanks for this. I have tried this approach, and I was getting a number of responses in the export method . I think now, I need to have it in a pipe(take(1)) which seems to have controlled that – ronoc4 Mar 07 '21 at 21:53