Home > Net >  Angular observable receives data only once onInit
Angular observable receives data only once onInit

Time:12-02

I am working my way through Moshs' Angular class building a simple shopping page. Since this tutorial is already quite old I had to work my way through the changes in bootstrap and angular and I did quite good until I got to the shopping cart section.

The issue I have here is, that I am using a mysql database instead of firebase and I my implementation to calculate the total number of items in my cart is not working as expected but I can not figure out why.

So what I am doing is the following: On my product page you can add / remove items to the cart - that data is sent to the server and after that I am returning the updated cart via a post request observable like this:

getCartContent(): Observable<ShoppingCart> {
  return this.getOrCreateCart().pipe(
      switchMap(cart => {
        return this.http.post<ShoppingCart>(this.url   'cart_api.php', JSON.stringify(
          [
            'getCartContent',
            {
              cartId: cart
            },
            ''
          ]
        ));
      })
    );
  }

I use a switchmap here since I have to get the cartId first and then get the respective cart items. This is working fine, I get the cart data updated on the product page and writing the new cart data to the localstorage / console shows me that the data is returnded as expected.

What I now try to do is to show the total number of items in the cart in the navbar. Therefor I subscribed also in the navbar.ts to this observable and wanted to re-calculate the amount every time the getCartContent() is executed.

My code in the navbar looks like this:

  ngOnInit() {
    this.ShoppingCartService.getCartContent().subscribe(cart => {
        console.log(cart); // just to see, if this subscription receives data
        this.shoppingCartItemsCount = 0;
        for (let productId in cart.ITEMS) {
          this.shoppingCartItemsCount  = Number(cart.ITEMS[productId]);
        }
      }
    );
  }

The result is, that the cart data is logged in the console correctly - but only once when I load the page - any change on the items in the cart are not reflected in the navbar subscription but working in the product page.

I am pretty sure I make a fallacy of thinking but I am totally stuck - any help is highly appreciated.

CodePudding user response:

I understand your confusion, actually, the getCartContent is called only once even though it returns an observable. What you need is a BehaviourSubject in your service class to reflect the changes. Below is a common practice how to implement it.

private cartContent$: BehaviourSubject<ShoppingCart> = new BehaviourSubject();
public listenToCartChanges(): Observable<ShoppingCart> {
  return this.cartContent$.asObservable();
}

In order to push the changes to the subject you can add a tap on your function to push the changes to the BehaviourSubject.

   getCartContent(): Observable<ShoppingCart> {
  return this.getOrCreateCart().pipe(
      switchMap(cart => {
        return this.http.post<ShoppingCart>(this.url   'cart_api.php', JSON.stringify(
          [
            'getCartContent',
            {
              cartId: cart
            },
            ''
          ]
        ));
      }),
      pipe(res => {
        this.cartContent$.next(res)
      }
    );
  }

Lastly, you need to subscribe to the function listenToCartChanges() in your nav component (don't forget to unsubscribe on ngOnDestroy to prevent memory leaks).

  • Related