Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { Injectable, inject, effect, computed } from '@angular/core';
- import {
- signalStore,
- withState,
- withMethods,
- patchState,
- withHooks,
- withComputed,
- } from '@ngrx/signals';
- import { rxMethod } from '@ngrx/signals/rxjs-interop';
- import { pipe, switchMap, tap, catchError, of } from 'rxjs';
- import { Product } from './product.model';
- import { ProductService } from './product.service';
- interface ProductState {
- products: Product[];
- isLoading: boolean;
- error: string | null;
- }
- const initialState: ProductState = {
- products: [],
- isLoading: false,
- error: null,
- };
- @Injectable({
- providedIn: 'root',
- })
- export class ProductStore extends signalStore(
- withState(initialState),
- withComputed((store) => ({
- lowStockProducts: computed(() =>
- store.products().filter((p) => p.stock < 10)
- ),
- })),
- withMethods((store, productService = inject(ProductService)) => ({
- loadProducts: rxMethod<void>(
- pipe(
- tap(() => patchState(store, { isLoading: true, error: null })),
- switchMap(() => productService.getAll()),
- tap((res) =>
- patchState(store, {
- products: res.products,
- isLoading: false,
- error: null,
- })
- ),
- catchError((error) => {
- patchState(store, {
- products: [],
- isLoading: false,
- error:
- error instanceof Error
- ? error.message
- : 'Failed to load products',
- });
- return of(null);
- })
- )
- ),
- addProduct(newProduct: Product): void {
- const currentProducts = store.products();
- const exists = currentProducts.some((p) => p.id === newProduct.id);
- if (exists) {
- patchState(store, {
- error: `Product with ID ${newProduct.id} already exists`,
- });
- return;
- }
- patchState(store, {
- products: [...currentProducts, newProduct],
- error: null,
- });
- },
- addProductToApi: rxMethod<Product>(
- pipe(
- tap(() => patchState(store, { isLoading: true, error: null })),
- switchMap((newProduct) => {
- // Check if product already exists locally first
- const currentProducts = store.products();
- const exists = currentProducts.some(
- (p) => p.title === newProduct.title
- );
- if (exists) {
- throw new Error(
- `Product with title "${newProduct.title}" already exists`
- );
- }
- return productService.create(newProduct);
- }),
- tap((createdProduct) => {
- const currentProducts = store.products();
- patchState(store, {
- products: [...currentProducts, createdProduct],
- isLoading: false,
- error: null,
- });
- }),
- catchError((error) => {
- patchState(store, {
- isLoading: false,
- error:
- error instanceof Error ? error.message : 'Failed to add product',
- });
- return of(null);
- })
- )
- ),
- updateProductToApi: rxMethod<{
- productId: number;
- updatedProduct: Partial<Product>;
- }>(
- pipe(
- tap(() => patchState(store, { isLoading: true, error: null })),
- switchMap(({ productId, updatedProduct }) => {
- // Check if product exists locally first
- const currentProducts = store.products();
- const productIndex = currentProducts.findIndex(
- (p) => p.id === productId
- );
- if (productIndex === -1) {
- throw new Error(`Product with ID ${productId} not found`);
- }
- return productService.update(productId, updatedProduct);
- }),
- tap((updatedProduct) => {
- const currentProducts = store.products();
- const productIndex = currentProducts.findIndex(
- (p) => p.id === updatedProduct.id
- );
- const updatedProducts = [...currentProducts];
- updatedProducts[productIndex] = updatedProduct;
- patchState(store, {
- products: updatedProducts,
- isLoading: false,
- error: null,
- });
- }),
- catchError((error) => {
- patchState(store, {
- isLoading: false,
- error:
- error instanceof Error
- ? error.message
- : 'Failed to update product',
- });
- return of(null);
- })
- )
- ),
- deleteProductToApi: rxMethod<number>(
- pipe(
- tap(() => patchState(store, { isLoading: true, error: null })),
- switchMap((productId) => {
- // Check if product exists locally first
- const currentProducts = store.products();
- const exists = currentProducts.some((p) => p.id === productId);
- if (!exists) {
- throw new Error(`Product with ID ${productId} not found`);
- }
- return productService.delete(productId).pipe(
- tap(() => productId), // Pass productId to next operator
- switchMap(() => of(productId)) // Return productId for the final tap
- );
- }),
- tap((productId) => {
- const currentProducts = store.products();
- patchState(store, {
- products: currentProducts.filter((p) => p.id !== productId),
- isLoading: false,
- error: null,
- });
- }),
- catchError((error) => {
- patchState(store, {
- isLoading: false,
- error:
- error instanceof Error
- ? error.message
- : 'Failed to delete product',
- });
- return of(null);
- })
- )
- ),
- updateProduct(productId: number, updatedProduct: Product): void {
- const currentProducts = store.products();
- const productIndex = currentProducts.findIndex((p) => p.id === productId);
- if (productIndex === -1) {
- patchState(store, {
- error: `Product with ID ${productId} not found`,
- });
- return;
- }
- const updatedProducts = [...currentProducts];
- updatedProducts[productIndex] = updatedProduct;
- patchState(store, {
- products: updatedProducts,
- error: null,
- });
- },
- deleteProduct(productId: number): void {
- const currentProducts = store.products();
- const exists = currentProducts.some((p) => p.id === productId);
- if (!exists) {
- patchState(store, {
- error: `Product with ID ${productId} not found`,
- });
- return;
- }
- patchState(store, {
- products: currentProducts.filter((p) => p.id !== productId),
- error: null,
- });
- },
- findProduct(productId: number): Product | undefined {
- return store.products().find((p) => p.id === productId);
- },
- productExists(productId: number): boolean {
- return store.products().some((p) => p.id === productId);
- },
- clearError(): void {
- patchState(store, { error: null });
- },
- reset(): void {
- patchState(store, initialState);
- },
- })),
- // ✅ AUTOMATIC SORTING using withHooks and effect
- withHooks({
- onInit(store) {
- effect(() => {
- const products = store.products();
- // Only sort if products exist and we have more than one product
- if (products && products.length > 1) {
- // Check if the array is already sorted to avoid unnecessary updates
- const sorted = [...products].sort((a, b) =>
- a.title.localeCompare(b.title)
- );
- // Only update if the order actually changed
- const needsSorting = products.some(
- (prod, index) => prod.id !== sorted[index]?.id
- );
- if (needsSorting) {
- console.log('Products not sorted, auto-sorting now...');
- patchState(store, { products: sorted });
- }
- }
- });
- },
- })
- ) {}
Advertisement
Add Comment
Please, Sign In to add comment