1

I have below calculate() function block where there is a function call inside an if condition to check whether an item exist using a api call.

But the method itemExistsInBasket() always returns false as the service call won't be finished and I will have to use await() to achieve this.

Could you please let me know a better way to solve the below problem.

calculate(){
     if(itemExistsInBasket()){
           // service call
     }
  }

itemExistsInBasket(){
  
    let itemExists = false;
    this._sharedService.post('API/checkItemInBask', requestBody).subscribe((res: any) => {
        itemExists = res.exists ? true : false;
    });

   return itemExists; // always returns false
}
ruth
  • 29,535
  • 4
  • 30
  • 57
Onera
  • 687
  • 3
  • 14
  • 34

2 Answers2

2

Short answer: you cannot.

The HTTP request is asynchronous. Angular has no clue when the HTTP request will respond. By the time you're doing return itemExists, the variable itemExists hasn't been updated inside the subscription yet and so it returns the previous value false.

See here for more info on how asynchronous data works.

You need subscribe where the response is required. Note that once a reactive paradigm is introduced into the app all it's direct dependents should be reactive as well.

Moreover it appears you're attempting to have nested subscriptions. Instead you need to use RxJS higher order mapping operator like switchMap to switch from one observable to another. You could also use RxJS filter pipe to avoid triggering the second call when the response of the first call is false.

Try the following

import { filter, switchMap } from 'rxjs/operators';

calculate() {
  this._sharedService.post('API/checkItemInBask', requestBody).pipe(
    filter(res => res.exists),       // <-- will emit only when `res.exists` is defined
    switchMap(_ => this._sharedService.someOtherReq())
  ).subscribe({
    next: res => { },
    error: error => { }
  });
}
ruth
  • 29,535
  • 4
  • 30
  • 57
1

Services in Angular should return Promise whenever they depend on async methods (such as API calls). You should subscribe on them in components and render pages accordingly to result.

I'm not sure what your code should do, so I'll provide another example:

code in service:

fetchItemsInBasket(): Promise<Item>{
 
    return this._sharedService.post('API/itemsInBasket', requestBody)
                 .pipe(...) // get list of items in basket
}

code in component:

itemsInBasket: List<ItemsInBasket> // this field should be used in html template

ngOnInit() {
  this.service.fetchItemsInBasket()
     .subsribe(items => this.itemsInBasket = items);
}

EDIT: I think I get what you are trying to accomplish. Lets say we have a component template with fragment like this:

<button *ngIf="!itemInBasket && baskeInformationReady" (click)="...">Add to basket</button>
<button *ngIf="itemInBasket && baskeInformationReady" disabled>Item already in basket</button>

In the .ts file with the component we should have field:

itemInBasket = false;
basketInformationReady = false;

and in ngOnInit whe should have:

ngOnInit() {
  this.service.itemExistsInBasket()
    .subscribe(res => {
        this.itemInBasket = res;
        this.basketInformationReady = true;
      });
}
  • Hi Jakub, what I am trying to do is, itemExistsInBasket function should check whether an item exists and if it does it should return true else false. but from my example it always returns false – Onera Nov 24 '20 at 12:52
  • @Onera What should eventually happen if the item exists? And what should happen if it does not? – Jakub Biernaczyk Nov 24 '20 at 12:55
  • If it exists, itemExistsInBasket() should return true else false. And If it is true my calculate() function will proceed further else I will not. But in my case as you know it always returns false as it is asnyc call – Onera Nov 24 '20 at 13:01
  • @Onera I think you should look at @Michael D answer. Whatever you mean as "proceed further" should be placed here: `next: res => { if (res) { } }` – Jakub Biernaczyk Nov 24 '20 at 13:09