0

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.

Juergen
  • 3
  • 2

1 Answers1

0

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).

Džan Operta
  • 399
  • 2
  • 11
  • 1
    Thanks a lot for your answer... it solved my issue... partially. After replacing your second pipe with tap and subscribing to "listenToCartChanges()" I get the cart content updated in the navbar. Nevertheless - the "new BehaviourSubject()" needs an initial value so I defined an empty cart object to get rid of the error messages: `initCart: ShoppingCart = { ID: "", ITEMS: [] }; cartContent$: BehaviorSubject=new BehaviorSubject(this.initCart); ` Now I have the issue that on first page loading the cart content is always 0 until I update the cart on the product page... – Juergen Dec 01 '22 at 19:02
  • 1
    Okay, forget my comment, issue solved by using the localstorage instead of creating an empty cart. Nevertheless - comming back to my initial question: why does the Observable not work so that I have to create a BehaviourSubject? – Juergen Dec 01 '22 at 19:22
  • The Observable from your HTTP request actually returns only one response, when the HTTP request is finished. This would be different if you would be continually getting values from the backend (e.g sockets) which is not the case with simple HTTP requests, they return only one response. On the other hand, you can see the BehaviourSubject as an observable stream, to which you can subscribe from more places by creating a subscription. Then if we push a value to the Subject with `.next()`, every subscriber will receive the new value. – Džan Operta Dec 02 '22 at 09:07
  • Btw as there exist more types of RxJs Subjects, for better general understanding, you can check this thread https://stackoverflow.com/questions/43118769/subject-vs-behaviorsubject-vs-replaysubject-in-angular. – Džan Operta Dec 02 '22 at 09:08