Angular 16: Troubleshooting RxJs Problem with Updating Total in Header Component

Hello, I'm facing an issue with my Observable and need some guidance.

Currently, I have a functional cart and checkout system that works well when adding items to the cart. It successfully manages total items and costs. These components are located as siblings within the shopping-cart folder structure. However, I am experiencing an inconsistency where the total is only updated correctly after reloading the page. There is an image next to the shopping cart which fails to update the total number of items until a reload is performed.

Here is a visual representation: https://i.sstatic.net/f5yCi2S6.png

The problematic area lies in the header component which resides in a separate folder named UI in the hierarchy. Despite attempting various solutions, the issue persists. Understanding this will not only help me resolve the current problem but also assist in implementing other updates in the header component such as displaying user login information without requiring a page reload. Any advice on how to rectify this through subscription handling would be highly appreciated. Below is the code snippet for the header component:

header.component.ts

   Component({
     providers:[NgbModal, NgbModalConfig, ProductItemComponent, ProductListComponent, 
     CartItemComponent, EventEmitter, CartComponent, WishlistListComponent], 
     //NgbModal is a service for modal dialog
     selector: 'app-header',
     templateUrl: './header.component.html',
     styleUrls: ['./header.component.scss']
  })

     constructor( private msg: MessengerService, private 
     cartService:CartService,private accountService: AccountService, private 
     modalService: NgbModal, private config: NgbModalConfig,
     private searchService:SearchService, private auth:AuthService, public 
     prodcomp:ProductItemComponent,
     private _imageService:ImageService, private formBuilder: 
     UntypedFormBuilder,private alertService: AlertService,  private _ 
     wishlistitemService: WishlistItemService)

   {

   //Attempted this approach without success      
    // this.cartService.itemTotals$.subscribe((v: any) => (this.itemTotal$ = v));
        
   }

     ngOnInit(): void {
       this.loadCartItems();
    }


     loadCartItems(){
      this.cartService.getCartItems().subscribe((item) => {
      this.cartitems$ = item;
      // alert("A cart item" + item);
      this.calNumberOfItems();
     })
   }



   calNumberOfItems(){;
     for(this.qty=0; this.qty < this.cartitems$.length; this.qty++){
      this.qty
   }
      this.itemTotal$ = this.qty; 

  }

header.html

 </span><span ><i class="fas fa-circle" aria-hidden="true" style="font-size: 25px; 
 margin-top: -20px; cursor:none; color:#1D004F;"><strong style="color:#ffffff; margin- 
 left: -18px; padding-top: 0; font-size:10px; text-align: center; line-height: 20px;"> 
 {{itemTotal$}}</strong></i></span></a>            

cart.service.ts

  export interface Cartitems{
  _id: string,
  name: string,
  size: string,
  qty: number,
  price: number,
  imageUrl:string
}



  @Injectable({
  providedIn: 'root'

 })

export class CartService {

  
 rootUrl = 'api';    

 cartitems$= new BehaviorSubject<Cartitems[]>([]);   //this works
  //itemTotals$  = new BehaviorSubject<Cartitems[]>([]);



  getCartItems(): Observable<Cartitems[]> {
   return this.http.get<Cartitems[]>(this.rootUrl + '/getProducts/cartitems')
    
  }

}

Only the relevant parts of the code were shared. If anyone could provide insights into what might be missing or causing the issue, it would be greatly appreciated. Thank you in advance.

Answer №1

To enable data sharing between components, it is recommended to utilize a dedicated service:

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Cartitems } from './cartitems.interface'; // Make sure the interface is defined correctly

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private cartItemsSource = new BehaviorSubject<Cartitems[]>([]);
  currentCartItems = this.cartItemsSource.asObservable();

  constructor() { }

  updateCartItems(cartItems: Cartitems[]) {
    this.cartItemsSource.next(cartItems);
  }
}

It's advisable to keep services for data sharing separate from those handling API requests to retrieve data from a database.

The following service is responsible for fetching saved items from the db:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DataService } from './data.service';
import { Cartitems } from './cartitems.interface';

@Injectable({
  providedIn: 'root'
})
export class CartService {

  constructor(private http: HttpClient, private dataService: DataService) { }

  getCartItems() {
    this.http.get<Cartitems[]>(this.rootUrl + '/getProducts/cartitems').subscribe(
      data => {
        this.dataService.updateCartItems(data);
      },
      error => {
        console.error('Error fetching cart items', error);
      }
    );
  }
}

The service that retrieves data from the db can rely on the data-sharing service so that fetched data can be published through the sharedata service.

The next component handles the request for carts from the backend. It serves as the main page where product retrieval occurs, or it could even function as a cart component. Any section requiring cart item retrieval different from the header component should use this component. If there are items you intend to add to your cart within this component, after adding them, utilize the dataService to update and publish the cart items. This ensures the header reflects the updates as well.

import { Component, OnInit } from '@angular/core';
import { CartService } from './cart.service';

@Component({
  selector: 'app-main',
  templateUrl: './main.component.html',
  styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {

  constructor(private cartService: CartService) { }

  ngOnInit(): void {
    this.cartService.getCartItems();
  }
}

This illustrates how to subscribe to the DataService in the HeaderComponent to facilitate shared data communication among components. Whenever another component in your Angular application updates the DataService, all subscribers will receive notifications.

import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';
import { Cartitems } from './cartitems.interface';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
  cartItems: Cartitems[] = [];

  constructor(private dataService: DataService) { }

  ngOnInit(): void {
    this.dataService.currentCartItems.subscribe(
      cartItems => {
        this.cartItems = cartItems;
      },
      error => {
        console.error('Error getting cart items', error);
      }
    );
  }
}

Answer №2

Big shoutout to Tomescu for shedding light on shared services! Even though it didn't quite fit my situation, it could be the perfect solution for someone else. I was too deep into the structure of my code to make the necessary adjustments. However, I did discover a cool trick to refresh totals from the header icon using Reactive Angular with RXJs auto refresh using a subject. Instead of modifying my own code, I decided to share a video link because every codebase is unique. I've received valuable guidance multiple times in this developer forum and want to pay it forward by sharing knowledge. Hopefully, this link will benefit many others as well. Link: Check out the video here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for enabling the TypeScript compiler to locate bokeh's "*.d.ts" files

I recently made the switch from Bokeh's convenient inline extension framework to their npm based out of line build system. I'm currently working on getting my extension to build, but I've noticed that Bokeh organizes all TypeScript *.ts.d fi ...

Can you explain the execution process of this Http.post method and provide details about the code path it follows

As I delve into the world of web development, one aspect that has me stumped is the functionality of the Http.post section within a project I stumbled upon on GitHub. Specifically, this pertains to an ExpressJS with Typescript repository I came across. So, ...

Ensure Angular Reactive Forms do not include empty fields when submitting the form

Is there a way to retrieve only the fields with values entered by the user from a form and exclude empty fields in the resulting JSON object? Currently, the JSON object still includes empty quotation marks for empty inputs. Thank you! Current: { "user ...

Transmit the timestamp information to Supabase using Next.js

Hello there! I'm facing an issue while attempting to send time data to supabase using next.js. Unfortunately, I haven't been successful in sending the data yet. I suspect that the problem lies in the type not being valid for supabase, but I&apos ...

The async and await functions do not necessarily wait for one another

I am working with Typescript and have the following code: import sql = require("mssql"); const config: sql.config = {.... } const connect = async() => { return new Promise((resolve, reject) => { new sql.ConnectionPool(config).connect((e ...

What is the process for importing Angular Material into a project?

After successfully installing Angular Material Design, I am now attempting to integrate it into my app.module.ts file: import { MaterialModule } from '@angular/material'; What should I specify within the imports: [] section in order to load all ...

Issue with Angular 2 in Visual Studio 2013: Pressing Ctrl+K+D causes formatting to change to lowercase

Please note that while this issue is not directly related to Angular2, anyone who uses Angular2 with Visual Studio 2013 may be able to help. Whenever I use the key combination Ctrl + K + D in Visual Studio 2013, it changes HTML or Angular2 directives/mark ...

Restricted inclusive collection utilizing embedded identifier

Attempting to segregate a discriminated union array into separate arrays of its union types has presented some challenges. I came across this particular question that provides generic discriminators. Unfortunately, the dataset I am working with doesn&apos ...

In Express, the async middleware is bypassed, allowing the next route to be executed seamlessly

Currently, I am in the process of developing an application similar to Zotero using express.js, but I have encountered a perplexing issue. Although I cannot pinpoint the exact problem, based on the logs I am receiving, it appears that my middlewares are n ...

Typescript's Class-method concept

I am interested in implementing a class-based method in Typescript where a method defined on a base class can access the subclass from which it was called. While this behavior is possible in Python, I have not found an equivalent in Typescript. What would ...

Ways to mimic an object and confirm that a specific parameter was passed to the mimic

I am currently experimenting with testing an HttpInterceptor, using ng-mocks: @Injectable() export class AuthTokenInterceptor implements HttpInterceptor { constructor(private store: Store) {} intercept(req: HttpRequest<any>, ...

Getting an error message with npm and Typescript that says: "Import statement cannot be used outside

After developing and publishing a package to npm, the code snippet below represents how it starts: import * as aws from "@pulumi/aws"; import * as pulumi from "@pulumi/pulumi"; export interface ... export class controlplaneDependencies ...

What steps should I take to troubleshoot an error with accessing 'request.body' within an async function that is returning a 'ReadableStream' object instead of the data I was expecting?

While developing my CRUD functionality in Next.js with TypeScript and Prisma, I encountered an issue with the PUT method. The GET method handler works fine, but for some reason, the PUT method is causing errors that I'm struggling to resolve. When in ...

Error: Undefined object trying to access 'vibrate' property

Good day, I apologize for my poor English. I am encountering an issue with Ionic Capacitor while attempting to utilize the Vibration plugin. The documentation lacks detailed information, and when checking the Android Studio terminal, I found the following ...

Tips for stopping TypeScript from halting Webpack compilation due to undefined variables stated in Webpack's ProvidePlugin

Is there a way to prevent WebPack's build process from failing when the TypeScript compiler throws errors about unresolved variables that are already configured in Webpack's ProvidePlugin settings? webpack.config.js plugins: [ ... new webpack.P ...

The file node_modules/@types/node/index.d.ts encountered an error with code TS1084, indicating that the 'reference' directive syntax used is invalid

Having some trouble with typescript compilation. Anyone else encountering this issue? node_modules/@types/node/index.d.ts(20,1): error TS1084: Invalid 'reference' directive syntax. Here is my tsconfig.json setup: { "compileOnSave" ...

Angular automatically scrolls to the top of the page when clicking on any part of the bottom

I am encountering an issue on a page where I have implemented NgFor. Whenever I click anywhere at the bottom of the page, it automatically jumps to the top. Additionally, when I try to click on a button at the bottom, the button's action does not exec ...

Encountering a TypeScript error while calling a Vue lifecycle hook method

Struggling to call a method in a Vue root component from a lifecycle method in typescript? See below for a simple example that showcases this issue: import Vue from "vue"; class Game { a: number; b: number; constructor() { this.a = 3; ...

Can you explain the purpose of the .json() function in Angular2?

Can you explain the purpose of the .json() function within http requests in Angular2? Here is an example code snippet: this.http.get('http://localhost:8080/getName') .subscribe(res => this.names = res.json()); Is it correct to assume that t ...

What is the best way to preserve all props while typing a styled component in Typescript?

I'm just starting out with styled components and I want to ensure that I can properly type my styled components so that I can easily utilize all the props I pass, not just the ones defined in the theme or through an interface. Is there a way to achie ...