Why is my stored product in localstorage overwriting the product in my service?

Discussão em 'Angular' iniciado por koque, Dezembro 21, 2024.

  1. koque

    koque Guest

    There are two ways the selected product in SelectedProductService is updated -- from localStorage when the user refreshes the SelectedProduct page and when the user selects a product in the ProductGallery page.

    When the user refreshes the SelectedProduct page, the stored product from localStorage is displayed so as not to display a blank page to the user. To accomplish this, I created app.service.ts which is called from app.component.ts.

    app.component.ts:

    export class AppComponent {
    private appService = inject(AppService);

    ngOnInit() {
    this.appService.restoreStateFromLocalStorage()
    }
    }


    app.service.ts:

    export class AppService {

    public appSignal = signal<{
    neiman: {
    selectedProduct: ProductModel;
    cart: ProductModel[];
    };
    }>({
    neiman: {
    selectedProduct: new ProductModel(),
    cart: [],
    },
    });

    public restoreStateFromLocalStorage = () => {
    const neiman = JSON.parse(localStorage.getItem('neiman') as string);
    console.log('neiman', neiman)
    this.appSignal.set({ neiman: { ...neiman } });
    console.log('appSignal', this.appSignal())
    };
    }


    AppComponent calls appService.restoreStateFromLocalstorage in its ngOnInit method. This method retrieves the stored selected product from localStorage and updates AppService's appSignal. SelectedProductService "subscribes" to appSignal so it receives a copy of the the stored Selected product in its effect which is saved to SelectedProductService's signal as shown:

    export class SelectedProductService {
    private appService = inject(AppService);
    public selectedProductSignal = signal(<ProductModel>
    new ProductModel(),
    );
    public selectedProduct?: ProductModel;

    private appEffect = effect(() => {
    let selectedProduct: ProductModel = this.appService.appSignal().neiman.selectedProduct;

    untracked(() => {
    this.selectedProductSignal.set({ ...selectedProduct })
    });

    console.log('SelectedProductService.selectProductSignal from localStorage', this.selectedProductSignal())

    });


    public setSelectedProduct = (selectedProduct: ProductModel) => {
    this.selectedProductSignal.set({ ...selectedProduct });
    console.log('SelectedProductService.selectedProductSignal', this.selectedProductSignal());
    saveStateToLocalStorage({ selectedProduct: this.selectedProductSignal() })
    };


    }

    This is the first way SelectedProductService's selectedProduct is updated. The second way involves the setSelectedProduct method shown above.

    In the ProductGallery page, the user may click on a product:

    <div
    class="col d-flex flex-column col-12 col-md-4 col-lg-3 mb-4 gallery-img-col"
    *ngFor="let product of products"
    >
    <img
    class="gallery-img"
    src="assets/img/{{ product.images[0] }}"
    (click)="setSelectedProduct(product)"
    />
    </div>


    Upon clicking on a product, the setSelectedProduct(product) function of ProductGalleryComponent is called. This function calls the setSelectedProduct function of ProductGalleryService then navigates to the SelectedProductComponent:

    public setSelectedProduct = (product: ProductModel) => {
    this.productGalleryService.setSelectedProduct(product);
    this.router.navigateByUrl('/selected-product');
    }


    The ProductGalleryServices setSelectedProduct function invokes the next method of the selectedProuctSubject.

    export class ProductGalleryService {
    public selectedProductSubject = new ReplaySubject<ProductModel>(1);
    selectedProductSubject$ = this.selectedProductSubject.asObservable();

    public setSelectedProduct = (product: ProductModel) => {
    this.selectedProductSubject.next(product);
    }
    }


    SelectedProductComponent subscribes to ProductGalleryService's selectedProductSubject hence gets a copy of the broadcast product. It then passes the product to SelectedProductService via it's setSelectedProduct method.

    export class SelectedProductComponent {
    private selectedProductEffect = effect(() => {
    this.selectedProduct = this.selectedProductService.selectedProductSignal();
    console.log('SelectedProductComponent.selectedProductXXX', this.selectedProduct)
    })

    constructor() {
    this.setSelectedProduct()
    }

    private setSelectedProduct = () => {
    this.productGalleryService.selectedProductSubject$.subscribe((product) => {
    console.log('selectedProductSubject.selectedProduct', product)
    this.selectedProductService.setSelectedProduct(product)
    })
    }
    }


    selectedProductService.setSelectedProduct(product) then updates the selectedProductService's signal:

    public setSelectedProduct = (selectedProduct: ProductModel) => {
    this.selectedProductSignal.set({ ...selectedProduct });
    console.log('SelectedProductService.selectedProductSignal', this.selectedProductSignal());
    saveStateToLocalStorage({ selectedProduct: this.selectedProductSignal() })
    };


    SelectedProductComponent gets a copy of the selected product in its effect as shown in the SelectedProductComponent above.

    The problem is whenever a product is selected in the ProductGalleryComponent page, somehow the selectedProduct is also brought in from localStorage superseding the product selected by the user. As the data flows show, data should only be brought in from localStorage when the browser is refreshed.

    Continue reading...

Compartilhe esta Página