Share
Advanced Signal Store: Structuring State for Real Angular Applications
Modern Angular applications grow quickly, and with that growth come new questions about how to structure and share state effectively. Signals make local state simple and expressive, but as applications evolve, architectural decisions around state become increasingly important.
In this talk, we go beyond the basics of the NgRx Signal Store and explore how it can be used as an architectural building block in real Angular applications. We look at practical scenarios and discuss where state belongs, how to define clear boundaries between components, features, and application wide concerns, and how Signal Store fits into that picture.
We dive deeper into advanced Signal Store concepts such as store composition, derived state, effects, and integration with Angular's dependency injection system, always from an architectural point of view rather than focusing only on the API.
This session is driven by experience and clear opinions. It focuses on sharing patterns, heuristics, and mental models that help teams structure state with confidence. You will leave with concrete ideas for designing maintainable and scalable Angular applications using Signal Store, grounded in real world constraints and everyday development.
Fabian Gosebrink
Transcript
99 slides
-
export class CheckoutService { readonly #baseUrl = 'http://localhost:3000/cart/'; readonly #http
= inject(HttpClient); readonly #cartProducts = signal<Product[]>([]); readonly #cartProductsChanged = new Subject<Product[]>(); cartProductsChanged = this.#cartProductsChanged.asObservable(); addToCart(product: Product) { // ... } getCartProducts() { // ... } removeFromCart(index: number) { // ... } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -
export class CheckoutService { readonly #baseUrl = 'http://localhost:3000/cart/'; readonly #http
= inject(HttpClient); readonly #cartProducts = signal<Product[]>([]); readonly #cartProductsChanged = new Subject<Product[]>(); cartProductsChanged = this.#cartProductsChanged.asObservable(); addToCart(product: Product) { // ... } getCartProducts() { // ... } removeFromCart(index: number) { // ... } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 readonly #baseUrl = 'http://localhost:3000/cart/'; readonly #http = inject(HttpClient); export class CheckoutService { 1 2 3 readonly #cartProducts = signal<Product[]>([]); 4 5 readonly #cartProductsChanged = new Subject<Product[]>(); 6 cartProductsChanged = this.#cartProductsChanged.asObservable(); 7 8 addToCart(product: Product) { 9 // ... 10 } 11 12 getCartProducts() { 13 // ... 14 } 15 16 removeFromCart(index: number) { 17 // ... 18 } 19 } 20 -
export class CheckoutService { readonly #baseUrl = 'http://localhost:3000/cart/'; readonly #http
= inject(HttpClient); readonly #cartProducts = signal<Product[]>([]); readonly #cartProductsChanged = new Subject<Product[]>(); cartProductsChanged = this.#cartProductsChanged.asObservable(); addToCart(product: Product) { // ... } getCartProducts() { // ... } removeFromCart(index: number) { // ... } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 readonly #baseUrl = 'http://localhost:3000/cart/'; readonly #http = inject(HttpClient); export class CheckoutService { 1 2 3 readonly #cartProducts = signal<Product[]>([]); 4 5 readonly #cartProductsChanged = new Subject<Product[]>(); 6 cartProductsChanged = this.#cartProductsChanged.asObservable(); 7 8 addToCart(product: Product) { 9 // ... 10 } 11 12 getCartProducts() { 13 // ... 14 } 15 16 removeFromCart(index: number) { 17 // ... 18 } 19 } 20 readonly #cartProducts = signal<Product[]>([]); export class CheckoutService { 1 readonly #baseUrl = 'http://localhost:3000/cart/'; 2 readonly #http = inject(HttpClient); 3 4 5 readonly #cartProductsChanged = new Subject<Product[]>(); 6 cartProductsChanged = this.#cartProductsChanged.asObservable(); 7 8 addToCart(product: Product) { 9 // ... 10 } 11 12 getCartProducts() { 13 // ... 14 } 15 16 removeFromCart(index: number) { 17 // ... 18 } 19 } 20 -
export class CheckoutService { readonly #baseUrl = 'http://localhost:3000/cart/'; readonly #http
= inject(HttpClient); readonly #cartProducts = signal<Product[]>([]); readonly #cartProductsChanged = new Subject<Product[]>(); cartProductsChanged = this.#cartProductsChanged.asObservable(); addToCart(product: Product) { // ... } getCartProducts() { // ... } removeFromCart(index: number) { // ... } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 readonly #baseUrl = 'http://localhost:3000/cart/'; readonly #http = inject(HttpClient); export class CheckoutService { 1 2 3 readonly #cartProducts = signal<Product[]>([]); 4 5 readonly #cartProductsChanged = new Subject<Product[]>(); 6 cartProductsChanged = this.#cartProductsChanged.asObservable(); 7 8 addToCart(product: Product) { 9 // ... 10 } 11 12 getCartProducts() { 13 // ... 14 } 15 16 removeFromCart(index: number) { 17 // ... 18 } 19 } 20 readonly #cartProducts = signal<Product[]>([]); export class CheckoutService { 1 readonly #baseUrl = 'http://localhost:3000/cart/'; 2 readonly #http = inject(HttpClient); 3 4 5 readonly #cartProductsChanged = new Subject<Product[]>(); 6 cartProductsChanged = this.#cartProductsChanged.asObservable(); 7 8 addToCart(product: Product) { 9 // ... 10 } 11 12 getCartProducts() { 13 // ... 14 } 15 16 removeFromCart(index: number) { 17 // ... 18 } 19 } 20 readonly #cartProductsChanged = new Subject<Product[]>(); cartProductsChanged = this.#cartProductsChanged.asObservable(); export class CheckoutService { 1 readonly #baseUrl = 'http://localhost:3000/cart/'; 2 readonly #http = inject(HttpClient); 3 readonly #cartProducts = signal<Product[]>([]); 4 5 6 7 8 addToCart(product: Product) { 9 // ... 10 } 11 12 getCartProducts() { 13 // ... 14 } 15 16 removeFromCart(index: number) { 17 // ... 18 } 19 } 20 -
@Component({ /* ... */ }) export class CheckoutComponent implements OnInit
{ cartProducts = signal<Product[]>([]); totalAmount = signal(0); private readonly toastrService = inject(ToastrService); private readonly checkoutService = inject(CheckoutService); ngOnInit(): void { merge( this.checkoutService.getCartProducts(), this.checkoutService.cartProductsChanged, ).subscribe((products) => { this.cartProducts.set(products); const totalAmount = this.calculateTotalAmount(products); this.totalAmount.set(totalAmount); }); } onRemoveClicked(index: number): void { // ... } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -
@Component({ /* ... */ }) export class CheckoutComponent implements OnInit
{ cartProducts = signal<Product[]>([]); totalAmount = signal(0); private readonly toastrService = inject(ToastrService); private readonly checkoutService = inject(CheckoutService); ngOnInit(): void { merge( this.checkoutService.getCartProducts(), this.checkoutService.cartProductsChanged, ).subscribe((products) => { this.cartProducts.set(products); const totalAmount = this.calculateTotalAmount(products); this.totalAmount.set(totalAmount); }); } onRemoveClicked(index: number): void { // ... } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 cartProducts = signal<Product[]>([]); totalAmount = signal(0); @Component({ /* ... */ }) 1 export class CheckoutComponent implements OnInit { 2 3 4 private readonly toastrService = inject(ToastrService); 5 private readonly checkoutService = inject(CheckoutService); 6 7 ngOnInit(): void { 8 merge( 9 this.checkoutService.getCartProducts(), 10 this.checkoutService.cartProductsChanged, 11 ).subscribe((products) => { 12 this.cartProducts.set(products); 13 const totalAmount = this.calculateTotalAmount(products); 14 this.totalAmount.set(totalAmount); 15 }); 16 } 17 18 onRemoveClicked(index: number): void { 19 // ... 20 } 21 } 22 -
@Component({ /* ... */ }) export class CheckoutComponent implements OnInit
{ cartProducts = signal<Product[]>([]); totalAmount = signal(0); private readonly toastrService = inject(ToastrService); private readonly checkoutService = inject(CheckoutService); ngOnInit(): void { merge( this.checkoutService.getCartProducts(), this.checkoutService.cartProductsChanged, ).subscribe((products) => { this.cartProducts.set(products); const totalAmount = this.calculateTotalAmount(products); this.totalAmount.set(totalAmount); }); } onRemoveClicked(index: number): void { // ... } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 cartProducts = signal<Product[]>([]); totalAmount = signal(0); @Component({ /* ... */ }) 1 export class CheckoutComponent implements OnInit { 2 3 4 private readonly toastrService = inject(ToastrService); 5 private readonly checkoutService = inject(CheckoutService); 6 7 ngOnInit(): void { 8 merge( 9 this.checkoutService.getCartProducts(), 10 this.checkoutService.cartProductsChanged, 11 ).subscribe((products) => { 12 this.cartProducts.set(products); 13 const totalAmount = this.calculateTotalAmount(products); 14 this.totalAmount.set(totalAmount); 15 }); 16 } 17 18 onRemoveClicked(index: number): void { 19 // ... 20 } 21 } 22 private readonly toastrService = inject(ToastrService); private readonly checkoutService = inject(CheckoutService); @Component({ /* ... */ }) 1 export class CheckoutComponent implements OnInit { 2 cartProducts = signal<Product[]>([]); 3 totalAmount = signal(0); 4 5 6 7 ngOnInit(): void { 8 merge( 9 this.checkoutService.getCartProducts(), 10 this.checkoutService.cartProductsChanged, 11 ).subscribe((products) => { 12 this.cartProducts.set(products); 13 const totalAmount = this.calculateTotalAmount(products); 14 this.totalAmount.set(totalAmount); 15 }); 16 } 17 18 onRemoveClicked(index: number): void { 19 // ... 20 } 21 } 22 -
@Component({ /* ... */ }) export class CheckoutComponent implements OnInit
{ cartProducts = signal<Product[]>([]); totalAmount = signal(0); private readonly toastrService = inject(ToastrService); private readonly checkoutService = inject(CheckoutService); ngOnInit(): void { merge( this.checkoutService.getCartProducts(), this.checkoutService.cartProductsChanged, ).subscribe((products) => { this.cartProducts.set(products); const totalAmount = this.calculateTotalAmount(products); this.totalAmount.set(totalAmount); }); } onRemoveClicked(index: number): void { // ... } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 cartProducts = signal<Product[]>([]); totalAmount = signal(0); @Component({ /* ... */ }) 1 export class CheckoutComponent implements OnInit { 2 3 4 private readonly toastrService = inject(ToastrService); 5 private readonly checkoutService = inject(CheckoutService); 6 7 ngOnInit(): void { 8 merge( 9 this.checkoutService.getCartProducts(), 10 this.checkoutService.cartProductsChanged, 11 ).subscribe((products) => { 12 this.cartProducts.set(products); 13 const totalAmount = this.calculateTotalAmount(products); 14 this.totalAmount.set(totalAmount); 15 }); 16 } 17 18 onRemoveClicked(index: number): void { 19 // ... 20 } 21 } 22 private readonly toastrService = inject(ToastrService); private readonly checkoutService = inject(CheckoutService); @Component({ /* ... */ }) 1 export class CheckoutComponent implements OnInit { 2 cartProducts = signal<Product[]>([]); 3 totalAmount = signal(0); 4 5 6 7 ngOnInit(): void { 8 merge( 9 this.checkoutService.getCartProducts(), 10 this.checkoutService.cartProductsChanged, 11 ).subscribe((products) => { 12 this.cartProducts.set(products); 13 const totalAmount = this.calculateTotalAmount(products); 14 this.totalAmount.set(totalAmount); 15 }); 16 } 17 18 onRemoveClicked(index: number): void { 19 // ... 20 } 21 } 22 merge( this.checkoutService.getCartProducts(), this.checkoutService.cartProductsChanged, ).subscribe((products) => { this.cartProducts.set(products); const totalAmount = this.calculateTotalAmount(products); this.totalAmount.set(totalAmount); }); @Component({ /* ... */ }) 1 export class CheckoutComponent implements OnInit { 2 cartProducts = signal<Product[]>([]); 3 totalAmount = signal(0); 4 private readonly toastrService = inject(ToastrService); 5 private readonly checkoutService = inject(CheckoutService); 6 7 ngOnInit(): void { 8 9 10 11 12 13 14 15 16 } 17 18 onRemoveClicked(index: number): void { 19 // ... 20 } 21 } 22 -
export class ShellComponent implements OnInit { cartProductsCount = signal(0); private
readonly checkoutService = inject(CheckoutService); ngOnInit() { this.checkoutService.getCartProducts().subscribe((products) => { this.cartProductsCount.set(products.length); }); this.checkoutService.cartProductsChanged.subscribe((products) => { this.cartProductsCount.set(products.length); }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -
export class ShellComponent implements OnInit { cartProductsCount = signal(0); private
readonly checkoutService = inject(CheckoutService); ngOnInit() { this.checkoutService.getCartProducts().subscribe((products) => { this.cartProductsCount.set(products.length); }); this.checkoutService.cartProductsChanged.subscribe((products) => { this.cartProductsCount.set(products.length); }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 cartProductsCount = signal(0); private readonly checkoutService = inject(CheckoutService); export class ShellComponent implements OnInit { 1 2 3 4 ngOnInit() { 5 this.checkoutService.getCartProducts().subscribe((products) => { 6 this.cartProductsCount.set(products.length); 7 }); 8 9 this.checkoutService.cartProductsChanged.subscribe((products) => { 10 this.cartProductsCount.set(products.length); 11 }); 12 } 13 } 14 -
export class ShellComponent implements OnInit { cartProductsCount = signal(0); private
readonly checkoutService = inject(CheckoutService); ngOnInit() { this.checkoutService.getCartProducts().subscribe((products) => { this.cartProductsCount.set(products.length); }); this.checkoutService.cartProductsChanged.subscribe((products) => { this.cartProductsCount.set(products.length); }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 cartProductsCount = signal(0); private readonly checkoutService = inject(CheckoutService); export class ShellComponent implements OnInit { 1 2 3 4 ngOnInit() { 5 this.checkoutService.getCartProducts().subscribe((products) => { 6 this.cartProductsCount.set(products.length); 7 }); 8 9 this.checkoutService.cartProductsChanged.subscribe((products) => { 10 this.cartProductsCount.set(products.length); 11 }); 12 } 13 } 14 this.checkoutService.getCartProducts().subscribe((products) => { this.cartProductsCount.set(products.length); }); this.checkoutService.cartProductsChanged.subscribe((products) => { this.cartProductsCount.set(products.length); }); export class ShellComponent implements OnInit { 1 cartProductsCount = signal(0); 2 private readonly checkoutService = inject(CheckoutService); 3 4 ngOnInit() { 5 6 7 8 9 10 11 12 } 13 } 14 -
export class ProductsComponent implements OnInit { products = signal<{ category:
string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -
export class ProductsComponent implements OnInit { products = signal<{ category:
string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 products = signal<{ category: string; products: Product[] }[]>([]); export class ProductsComponent implements OnInit { 1 2 private readonly productsService = inject(ProductsService); 3 private readonly checkoutService = inject(CheckoutService); 4 private readonly toastrService = inject(ToastrService); 5 private readonly router = inject(Router); 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {} 22 -
export class ProductsComponent implements OnInit { products = signal<{ category:
string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 products = signal<{ category: string; products: Product[] }[]>([]); export class ProductsComponent implements OnInit { 1 2 private readonly productsService = inject(ProductsService); 3 private readonly checkoutService = inject(CheckoutService); 4 private readonly toastrService = inject(ToastrService); 5 private readonly router = inject(Router); 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {} 22 private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); export class ProductsComponent implements OnInit { 1 products = signal<{ category: string; products: Product[] }[]>([]); 2 3 4 5 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {} 22 -
export class ProductsComponent implements OnInit { products = signal<{ category:
string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 products = signal<{ category: string; products: Product[] }[]>([]); export class ProductsComponent implements OnInit { 1 2 private readonly productsService = inject(ProductsService); 3 private readonly checkoutService = inject(CheckoutService); 4 private readonly toastrService = inject(ToastrService); 5 private readonly router = inject(Router); 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {} 22 private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); export class ProductsComponent implements OnInit { 1 products = signal<{ category: string; products: Product[] }[]>([]); 2 3 4 5 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {} 22 this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {} ); private readonly toastrService = inject(ToastrService); 5 private readonly router = inject(Router); 6 7 ngOnInit() { 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const groupedByCategory = Object.keys(categoryProductMap).map( 25 (category) => ({ 26 category: CATEGORY NAME MAP[category as ProductCategory], 27 -
export class ProductsComponent implements OnInit { products = signal<{ category:
string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 products = signal<{ category: string; products: Product[] }[]>([]); export class ProductsComponent implements OnInit { 1 2 private readonly productsService = inject(ProductsService); 3 private readonly checkoutService = inject(CheckoutService); 4 private readonly toastrService = inject(ToastrService); 5 private readonly router = inject(Router); 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {} 22 private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); export class ProductsComponent implements OnInit { 1 products = signal<{ category: string; products: Product[] }[]>([]); 2 3 4 5 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {} 22 this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {} export class ProductsComponent implements OnInit { 1 products = signal<{ category: string; products: Product[] }[]>([]); 2 private readonly productsService = inject(ProductsService); 3 private readonly checkoutService = inject(CheckoutService); 4 private readonly toastrService = inject(ToastrService); 5 private readonly router = inject(Router); 6 7 ngOnInit() { 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const groupedByCategory = Object.keys(categoryProductMap).map( (category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: categoryProductMap[category] }) ); this.products.set(groupedByCategory); 17 result[category].push(product); 18 19 return result; 20 }, 21 {} 22 ); 23 24 25 26 27 28 29 30 31 32 }); 33 } 34 35 onProductClicked(id: string): void { 36 this.router.navigate(['products', id]); 37 } 38 39 -
src/ └── app/ ├── features/ │ └── ... └── shared/
├── services/ └── store/ 1 2 3 4 5 6 7 -
src/ └── app/ ├── features/ │ └── ... └── shared/
├── services/ └── store/ 1 2 3 4 5 6 7 └── app/ └── shared/ └── store/ src/ 1 2 ├── features/ 3 │ └── ... 4 5 ├── services/ 6 7 -
features └── checkout ├── ... └── store └── checkout-feature.store.ts 1
2 3 4 5 └── store └── checkout-feature.store.ts features 1 └── checkout 2 ├── ... 3 4 5 -
│ ├── checkout.html/ts/... │ └── checkout.store.ts features 1 └── checkout
2 ├── container 3 4 5 ├── ... 6 └── store 7 └── checkout-feature.store.ts 8 -
Count of Checkout Products Products Shell Checkout Product Details Shared
All Products Checkout Products -
Count of Checkout Products Single Product Products Shell Checkout Product
Details Shared All Products Checkout Products -
Count of Checkout Products Single Product Products Shell Checkout Product
Details Shared All Products Total Amount Checkout Products -
export const GlobalCheckoutStore = signalStore( { providedIn: 'root' }, withState({
products: [] as Product[] }), withMethods( ( store, toastrService = inject(ToastrService), checkoutService = inject(CheckoutService) ) => ({ addToCart(product: Product) { ... }, removeFromCart(index: number) { ... } }) ) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -
export const GlobalCheckoutStore = signalStore( { providedIn: 'root' }, withState({
products: [] as Product[] }), withMethods( ( store, toastrService = inject(ToastrService), checkoutService = inject(CheckoutService) ) => ({ addToCart(product: Product) { ... }, removeFromCart(index: number) { ... } }) ) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 toastrService = inject(ToastrService), checkoutService = inject(CheckoutService) export const GlobalCheckoutStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ products: [] as Product[] }), 3 withMethods( 4 ( 5 store, 6 7 8 ) => ({ 9 addToCart(product: Product) { ... }, 10 removeFromCart(index: number) { ... } 11 }) 12 ) 13 ); 14 -
export const ShellStore = signalStore( withComputed(( _, globalCheckoutStore = inject(GlobalCheckoutStore))
=> ({ cartProductsCount: computed(() => globalCheckoutStore.products().length) })) ); 1 2 3 4 5 6 7 8 -
@Component({ template: ` <app-header [cartProductsCount]="cartProductsCount()" /> ... `, }) export
class ShellComponent implements OnInit { cartProductsCount = signal(0); private readonly checkoutService = inject(CheckoutService); ngOnInit() { this.checkoutService.getCartProducts().subscribe((products) => { this.cartProductsCount.set(products.length); }); this.checkoutService.cartProductsChanged.subscribe((products) => { this.cartProductsCount.set(products.length); }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -
@Component({ template: ` <app-header [cartProductsCount]="store.cartProductsCount()" /> ... `, styleUrl: './shell.component.scss',
providers: [ShellStore] }) export class ShellComponent { store = inject(ShellStore); } 1 2 3 4 5 6 7 8 9 10 11 -
@Component({ template: ` <app-header [cartProductsCount]="store.cartProductsCount()" /> ... `, styleUrl: './shell.component.scss',
providers: [ShellStore] }) export class ShellComponent { store = inject(ShellStore); } 1 2 3 4 5 6 7 8 9 10 11 providers: [ShellStore] @Component({ 1 template: ` 2 <app-header [cartProductsCount]="store.cartProductsCount()" /> 3 ... 4 `, 5 styleUrl: './shell.component.scss', 6 7 }) 8 export class ShellComponent { 9 store = inject(ShellStore); 10 } 11 -
@Component({ template: ` <app-header [cartProductsCount]="store.cartProductsCount()" /> ... `, styleUrl: './shell.component.scss',
providers: [ShellStore] }) export class ShellComponent { store = inject(ShellStore); } 1 2 3 4 5 6 7 8 9 10 11 providers: [ShellStore] @Component({ 1 template: ` 2 <app-header [cartProductsCount]="store.cartProductsCount()" /> 3 ... 4 `, 5 styleUrl: './shell.component.scss', 6 7 }) 8 export class ShellComponent { 9 store = inject(ShellStore); 10 } 11 <app-header [cartProductsCount]="store.cartProductsCount()" /> store = inject(ShellStore); @Component({ 1 template: ` 2 3 ... 4 `, 5 styleUrl: './shell.component.scss', 6 providers: [ShellStore] 7 }) 8 export class ShellComponent { 9 10 } 11 -
export const ProductsStore = signalStore( withState({ products: [] as Product[]
}), withComputed((store) => ({ productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ getAll() // ..., 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -
export const ProductsStore = signalStore( withState({ products: [] as Product[]
}), withComputed((store) => ({ productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ getAll() // ..., 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 withState({ products: [] as Product[] }), export const ProductsStore = signalStore( 1 2 withComputed((store) => ({ 3 productsByCategories: computed(() => { 4 const products = store.products(); 5 const productsByCategory = ...; 6 7 const categories = Object.keys(productsByCategory); 8 9 return categories.map((category) => ({ 10 category: CATEGORY_NAME_MAP[category as ProductCategory], 11 products: productsByCategory[category] 12 })); 13 }) 14 })), 15 withMethods( 16 ( 17 store, 18 globalCheckoutStore = inject(GlobalCheckoutStore), 19 router = inject(Router) 20 ) => ({ 21 getAll() // ..., 22 -
export const ProductsStore = signalStore( withState({ products: [] as Product[]
}), withComputed((store) => ({ productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ getAll() // ..., 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 withState({ products: [] as Product[] }), export const ProductsStore = signalStore( 1 2 withComputed((store) => ({ 3 productsByCategories: computed(() => { 4 const products = store.products(); 5 const productsByCategory = ...; 6 7 const categories = Object.keys(productsByCategory); 8 9 return categories.map((category) => ({ 10 category: CATEGORY_NAME_MAP[category as ProductCategory], 11 products: productsByCategory[category] 12 })); 13 }) 14 })), 15 withMethods( 16 ( 17 store, 18 globalCheckoutStore = inject(GlobalCheckoutStore), 19 router = inject(Router) 20 ) => ({ 21 getAll() // ..., 22 productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) export const ProductsStore = signalStore( 1 withState({ products: [] as Product[] }), 2 withComputed((store) => ({ 3 4 5 6 7 8 9 10 11 12 13 14 })), 15 withMethods( 16 ( 17 store, 18 globalCheckoutStore = inject(GlobalCheckoutStore), 19 router = inject(Router) 20 ) => ({ 21 getAll() // ..., 22 -
export const ProductsStore = signalStore( withState({ products: [] as Product[]
}), withComputed((store) => ({ productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ getAll() // ..., 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 withState({ products: [] as Product[] }), export const ProductsStore = signalStore( 1 2 withComputed((store) => ({ 3 productsByCategories: computed(() => { 4 const products = store.products(); 5 const productsByCategory = ...; 6 7 const categories = Object.keys(productsByCategory); 8 9 return categories.map((category) => ({ 10 category: CATEGORY_NAME_MAP[category as ProductCategory], 11 products: productsByCategory[category] 12 })); 13 }) 14 })), 15 withMethods( 16 ( 17 store, 18 globalCheckoutStore = inject(GlobalCheckoutStore), 19 router = inject(Router) 20 ) => ({ 21 getAll() // ..., 22 productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) export const ProductsStore = signalStore( 1 withState({ products: [] as Product[] }), 2 withComputed((store) => ({ 3 4 5 6 7 8 9 10 11 12 13 14 })), 15 withMethods( 16 ( 17 store, 18 globalCheckoutStore = inject(GlobalCheckoutStore), 19 router = inject(Router) 20 ) => ({ 21 getAll() // ..., 22 export const ProductsStore = signalStore( withState({ products: [] as Product[] }), withComputed((store) => ({ productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ getAll() // ..., 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -
@Component({ // ... }) export class ProductsComponent implements OnInit {
products = signal<{ category: string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -
@Component({ // ... providers: [ProductsStore] }) export class ProductsComponent {
store = inject(ProductsStore); } 1 2 3 4 5 6 7 -
Count of Checkout Products Single Product Products Shell Checkout Product
Details Shared All Products Total Amount of Checkout Products Checkout Products -
Single Product Products Checkout Products Shell Checkout Product Details Shared
All Products All Products Count of Checkout Products Total Amount of Checkout Products -
app/ ├── features/ │ └── ... └── shared/ ├── services/
└── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 -
app/ ├── features/ │ └── ... └── shared/ ├── services/
└── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 ├── features/ ├── services/ ├── global-checkout.store.ts └── global-products.store.ts app/ 1 2 │ └── ... 3 └── shared/ 4 5 └── store/ 6 7 8 -
export const GlobalProductsStore = signalStore( { providedIn: 'root' }, withState({
products: [] as Product[] }), withMethods( (store, productsService = inject(ProductsService)) => ({ getAll: rxMethod<void>( // Get all products from backend ), add(product: Product) { patchState(store, { products: [...store.products(), product] }); }, addMany(products: Product[]) { patchState(store, { products }); } }) ) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -
export const GlobalProductsStore = signalStore( { providedIn: 'root' }, withState({
products: [] as Product[] }), withMethods( (store, productsService = inject(ProductsService)) => ({ getAll: rxMethod<void>( // Get all products from backend ), add(product: Product) { patchState(store, { products: [...store.products(), product] }); }, addMany(products: Product[]) { patchState(store, { products }); } }) ) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 withState({ products: [] as Product[] }), export const GlobalProductsStore = signalStore( 1 { providedIn: 'root' }, 2 3 withMethods( 4 (store, productsService = inject(ProductsService)) => ({ 5 getAll: rxMethod<void>( 6 // Get all products from backend 7 ), 8 add(product: Product) { 9 patchState(store, { products: [...store.products(), product] }); 10 }, 11 addMany(products: Product[]) { 12 patchState(store, { products }); 13 } 14 }) 15 ) 16 ); 17 -
export const GlobalCheckoutStore = signalStore( { providedIn: 'root' }, withState({
productIds: [] as string[] }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ products: computed(() => { // Match lokal IDs with global Products/IDs }) })), withMethods( ( store, toastrService = inject(ToastrService), checkoutService = inject(CheckoutService), globalProductsStore = inject(GlobalProductsStore) ) => ({ addToCart(product: Product) { ... }, removeFromCart(index: number) { ... }, loadCheckoutProductsIfNotLoaded: rxMethod<void>( pipe( filter(() => !globalProductsStore.products().length), exhaustMap(() => checkoutService.getCartProducts().pipe( tapResponse({ next: (products) => { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 -
export const GlobalCheckoutStore = signalStore( { providedIn: 'root' }, withState({
productIds: [] as string[] }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ products: computed(() => { // Match lokal IDs with global Products/IDs }) })), withMethods( ( store, toastrService = inject(ToastrService), checkoutService = inject(CheckoutService), globalProductsStore = inject(GlobalProductsStore) ) => ({ addToCart(product: Product) { ... }, removeFromCart(index: number) { ... }, loadCheckoutProductsIfNotLoaded: rxMethod<void>( pipe( filter(() => !globalProductsStore.products().length), exhaustMap(() => checkoutService.getCartProducts().pipe( tapResponse({ next: (products) => { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { providedIn: 'root' }, withState({ productIds: [] as string[] }), export const GlobalCheckoutStore = signalStore( 1 2 3 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 4 products: computed(() => { 5 // Match lokal IDs with global Products/IDs 6 }) 7 })), 8 withMethods( 9 ( 10 store, 11 toastrService = inject(ToastrService), 12 checkoutService = inject(CheckoutService), 13 globalProductsStore = inject(GlobalProductsStore) 14 ) => ({ 15 addToCart(product: Product) { ... }, 16 removeFromCart(index: number) { ... }, 17 loadCheckoutProductsIfNotLoaded: rxMethod<void>( 18 pipe( 19 filter(() => !globalProductsStore.products().length), 20 exhaustMap(() => 21 checkoutService.getCartProducts().pipe( 22 tapResponse({ 23 next: (products) => { 24 -
export const GlobalCheckoutStore = signalStore( { providedIn: 'root' }, withState({
productIds: [] as string[] }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ products: computed(() => { // Match lokal IDs with global Products/IDs }) })), withMethods( ( store, toastrService = inject(ToastrService), checkoutService = inject(CheckoutService), globalProductsStore = inject(GlobalProductsStore) ) => ({ addToCart(product: Product) { ... }, removeFromCart(index: number) { ... }, loadCheckoutProductsIfNotLoaded: rxMethod<void>( pipe( filter(() => !globalProductsStore.products().length), exhaustMap(() => checkoutService.getCartProducts().pipe( tapResponse({ next: (products) => { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { providedIn: 'root' }, withState({ productIds: [] as string[] }), export const GlobalCheckoutStore = signalStore( 1 2 3 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 4 products: computed(() => { 5 // Match lokal IDs with global Products/IDs 6 }) 7 })), 8 withMethods( 9 ( 10 store, 11 toastrService = inject(ToastrService), 12 checkoutService = inject(CheckoutService), 13 globalProductsStore = inject(GlobalProductsStore) 14 ) => ({ 15 addToCart(product: Product) { ... }, 16 removeFromCart(index: number) { ... }, 17 loadCheckoutProductsIfNotLoaded: rxMethod<void>( 18 pipe( 19 filter(() => !globalProductsStore.products().length), 20 exhaustMap(() => 21 checkoutService.getCartProducts().pipe( 22 tapResponse({ 23 next: (products) => { 24 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ export const GlobalCheckoutStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ productIds: [] as string[] }), 3 4 products: computed(() => { 5 // Match lokal IDs with global Products/IDs 6 }) 7 })), 8 withMethods( 9 ( 10 store, 11 toastrService = inject(ToastrService), 12 checkoutService = inject(CheckoutService), 13 globalProductsStore = inject(GlobalProductsStore) 14 ) => ({ 15 addToCart(product: Product) { ... }, 16 removeFromCart(index: number) { ... }, 17 loadCheckoutProductsIfNotLoaded: rxMethod<void>( 18 pipe( 19 filter(() => !globalProductsStore.products().length), 20 exhaustMap(() => 21 checkoutService.getCartProducts().pipe( 22 tapResponse({ 23 next: (products) => { 24 -
export const ProductsStore = signalStore( withState({ products: [] as Product[]
}), withComputed((store) => ({ productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ getAll() // ..., 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -
export const ProductsStore = signalStore( withState({ products: [] as Product[]
}), withComputed((store) => ({ productsByCategories: computed(() => { const products = store.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return categories.map((category) => ({ category: CATEGORY_NAME_MAP[category as ProductCategory], products: productsByCategory[category] })); }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ getAll() // ..., 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 withState({ products: [] as Product[] }), export const ProductsStore = signalStore( 1 2 withComputed((store) => ({ 3 productsByCategories: computed(() => { 4 const products = store.products(); 5 const productsByCategory = ...; 6 7 const categories = Object.keys(productsByCategory); 8 9 return categories.map((category) => ({ 10 category: CATEGORY_NAME_MAP[category as ProductCategory], 11 products: productsByCategory[category] 12 })); 13 }) 14 })), 15 withMethods( 16 ( 17 store, 18 globalCheckoutStore = inject(GlobalCheckoutStore), 19 router = inject(Router) 20 ) => ({ 21 getAll() // ..., 22 -
export const ProductsStore = signalStore( withComputed((store, globalProductsStore = inject(GlobalProductsStore)) =>
({ productsByCategories: computed(() => { const products = globalProductsStore.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return // ...; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ addToCart: globalCheckoutStore.addToCart, onProductClicked(id: string): void { router.navigate(['products', id]); } }) ), withHooks({ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 -
export const ProductsStore = signalStore( withComputed((store, globalProductsStore = inject(GlobalProductsStore)) =>
({ productsByCategories: computed(() => { const products = globalProductsStore.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return // ...; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ addToCart: globalCheckoutStore.addToCart, onProductClicked(id: string): void { router.navigate(['products', id]); } }) ), withHooks({ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ export const ProductsStore = signalStore( 1 2 productsByCategories: computed(() => { 3 const products = globalProductsStore.products(); 4 const productsByCategory = ...; 5 6 const categories = Object.keys(productsByCategory); 7 8 return // ...; 9 }) 10 })), 11 withMethods( 12 ( 13 store, 14 globalCheckoutStore = inject(GlobalCheckoutStore), 15 router = inject(Router) 16 ) => ({ 17 addToCart: globalCheckoutStore.addToCart, 18 onProductClicked(id: string): void { 19 router.navigate(['products', id]); 20 } 21 }) 22 ), 23 withHooks({ 24 -
export const ProductsStore = signalStore( withComputed((store, globalProductsStore = inject(GlobalProductsStore)) =>
({ productsByCategories: computed(() => { const products = globalProductsStore.products(); const productsByCategory = ...; const categories = Object.keys(productsByCategory); return // ...; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), router = inject(Router) ) => ({ addToCart: globalCheckoutStore.addToCart, onProductClicked(id: string): void { router.navigate(['products', id]); } }) ), withHooks({ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ export const ProductsStore = signalStore( 1 2 productsByCategories: computed(() => { 3 const products = globalProductsStore.products(); 4 const productsByCategory = ...; 5 6 const categories = Object.keys(productsByCategory); 7 8 return // ...; 9 }) 10 })), 11 withMethods( 12 ( 13 store, 14 globalCheckoutStore = inject(GlobalCheckoutStore), 15 router = inject(Router) 16 ) => ({ 17 addToCart: globalCheckoutStore.addToCart, 18 onProductClicked(id: string): void { 19 router.navigate(['products', id]); 20 } 21 }) 22 ), 23 withHooks({ 24 withHooks({ onInit(_, globalProductsStore = inject(GlobalProductsStore)) { globalProductsStore.getAll(); } }) 6 const categories = Object.keys(productsByCategory); 7 8 return // ...; 9 }) 10 })), 11 withMethods( 12 ( 13 store, 14 globalCheckoutStore = inject(GlobalCheckoutStore), 15 router = inject(Router) 16 ) => ({ 17 addToCart: globalCheckoutStore.addToCart, 18 onProductClicked(id: string): void { 19 router.navigate(['products', id]); 20 } 21 }) 22 ), 23 24 25 26 27 28 ); 29 -
export const ProductDetailStore = signalStore( withState({ productId: null as string
| null }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), globalProductsStore = inject(GlobalProductsStore), productDetailService = inject(ProductDetailService) ) => ({ addToCart: globalCheckoutStore.addToCart, loadProductIfNotLoaded: rxMethod<string>( pipe( tap((productId) => patchState(store, { productId })), 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 -
export const ProductDetailStore = signalStore( withState({ productId: null as string
| null }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), globalProductsStore = inject(GlobalProductsStore), productDetailService = inject(ProductDetailService) ) => ({ addToCart: globalCheckoutStore.addToCart, loadProductIfNotLoaded: rxMethod<string>( pipe( tap((productId) => patchState(store, { productId })), 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 withState({ productId: null as string | null }), export const ProductDetailStore = signalStore( 1 2 3 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 -
export const ProductDetailStore = signalStore( withState({ productId: null as string
| null }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), globalProductsStore = inject(GlobalProductsStore), productDetailService = inject(ProductDetailService) ) => ({ addToCart: globalCheckoutStore.addToCart, loadProductIfNotLoaded: rxMethod<string>( pipe( tap((productId) => patchState(store, { productId })), 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 withState({ productId: null as string | null }), export const ProductDetailStore = signalStore( 1 2 3 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 5 6 7 8 9 10 11 12 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 -
export const ProductDetailStore = signalStore( withState({ productId: null as string
| null }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), globalProductsStore = inject(GlobalProductsStore), productDetailService = inject(ProductDetailService) ) => ({ addToCart: globalCheckoutStore.addToCart, loadProductIfNotLoaded: rxMethod<string>( pipe( tap((productId) => patchState(store, { productId })), 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 withState({ productId: null as string | null }), export const ProductDetailStore = signalStore( 1 2 3 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 5 6 7 8 9 10 11 12 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 loadProductIfNotLoaded: rxMethod<string>( 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 filter((productId) => !globalProductsStore.products() 25 .find(x => x.id === productId)), 26 exhaustMap((id) => 27 productDetailService.loadProductDetail(id).pipe( 28 tapResponse({ 29 next: (product) => globalProductsStore.add(product), 30 error: console.error 31 }) 32 ) 33 -
export const ProductDetailStore = signalStore( withState({ productId: null as string
| null }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), globalProductsStore = inject(GlobalProductsStore), productDetailService = inject(ProductDetailService) ) => ({ addToCart: globalCheckoutStore.addToCart, loadProductIfNotLoaded: rxMethod<string>( pipe( tap((productId) => patchState(store, { productId })), 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 withState({ productId: null as string | null }), export const ProductDetailStore = signalStore( 1 2 3 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 5 6 7 8 9 10 11 12 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 loadProductIfNotLoaded: rxMethod<string>( export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 tap((productId) => patchState(store, { productId })), }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 24 filter((productId) => !globalProductsStore.products() 25 .find(x => x.id === productId)), 26 exhaustMap((id) => 27 productDetailService.loadProductDetail(id).pipe( 28 tapResponse({ 29 next: (product) => globalProductsStore.add(product), 30 error: console.error 31 }) 32 ) 33 ) 34 ) 35 -
export const ProductDetailStore = signalStore( withState({ productId: null as string
| null }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), globalProductsStore = inject(GlobalProductsStore), productDetailService = inject(ProductDetailService) ) => ({ addToCart: globalCheckoutStore.addToCart, loadProductIfNotLoaded: rxMethod<string>( pipe( tap((productId) => patchState(store, { productId })), 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 withState({ productId: null as string | null }), export const ProductDetailStore = signalStore( 1 2 3 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 5 6 7 8 9 10 11 12 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 loadProductIfNotLoaded: rxMethod<string>( export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 tap((productId) => patchState(store, { productId })), export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 24 filter((productId) => !globalProductsStore.products() .find(x => x.id === productId)), withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 25 26 exhaustMap((id) => 27 productDetailService.loadProductDetail(id).pipe( 28 tapResponse({ 29 next: (product) => globalProductsStore.add(product), 30 error: console.error 31 }) 32 ) 33 ) 34 ) 35 ) 36 }) 37 -
export const ProductDetailStore = signalStore( withState({ productId: null as string
| null }), withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), withMethods( ( store, globalCheckoutStore = inject(GlobalCheckoutStore), globalProductsStore = inject(GlobalProductsStore), productDetailService = inject(ProductDetailService) ) => ({ addToCart: globalCheckoutStore.addToCart, loadProductIfNotLoaded: rxMethod<string>( pipe( tap((productId) => patchState(store, { productId })), 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 withState({ productId: null as string | null }), export const ProductDetailStore = signalStore( 1 2 3 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ productDetail: computed(() => { const productId = store.productId(); const existingProduct = globalProductsStore.products() .find(x => x.id === productId); return existingProduct ?? null; }) })), export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 5 6 7 8 9 10 11 12 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 loadProductIfNotLoaded: rxMethod<string>( export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 tap((productId) => patchState(store, { productId })), export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 24 export const ProductDetailStore = signalStore( 1 withState({ 2 productId: null as string | null 3 }), 4 withComputed((store, globalProductsStore = inject(GlobalProductsStore)) => ({ 5 productDetail: computed(() => { 6 const productId = store.productId(); 7 const existingProduct = globalProductsStore.products() 8 .find(x => x.id === productId); 9 10 return existingProduct ?? null; 11 }) 12 })), 13 withMethods( 14 ( 15 store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 exhaustMap((id) => productDetailService.loadProductDetail(id).pipe( tapResponse({ next: (product) => globalProductsStore.add(product), error: console.error }) ) ) store, 16 globalCheckoutStore = inject(GlobalCheckoutStore), 17 globalProductsStore = inject(GlobalProductsStore), 18 productDetailService = inject(ProductDetailService) 19 ) => ({ 20 addToCart: globalCheckoutStore.addToCart, 21 loadProductIfNotLoaded: rxMethod<string>( 22 pipe( 23 tap((productId) => patchState(store, { productId })), 24 filter((productId) => !globalProductsStore.products() 25 .find(x => x.id === productId)), 26 27 28 29 30 31 32 33 34 ) 35 ) 36 }) 37 ) 38 ); 39 -
src/ ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/...
│ │ └── products.store.ts │ └── presentational/ ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 -
src/ ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/...
│ │ └── products.store.ts │ └── presentational/ ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/... │ │ └── products.store.ts │ └── presentational/ src/ 1 2 3 4 5 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 -
src/ ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/...
│ │ └── products.store.ts │ └── presentational/ ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/... │ │ └── products.store.ts │ └── presentational/ src/ 1 2 3 4 5 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 7 8 9 10 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 -
src/ ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/...
│ │ └── products.store.ts │ └── presentational/ ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/... │ │ └── products.store.ts │ └── presentational/ src/ 1 2 3 4 5 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 7 8 9 10 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 12 13 14 15 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 -
src/ ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/...
│ │ └── products.store.ts │ └── presentational/ ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/... │ │ └── products.store.ts │ └── presentational/ src/ 1 2 3 4 5 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 7 8 9 10 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 12 13 14 15 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 17 18 19 20 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 -
src/ ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/...
│ │ └── products.store.ts │ └── presentational/ ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/... │ │ └── products.store.ts │ └── presentational/ src/ 1 2 3 4 5 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 7 8 9 10 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 12 13 14 15 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 17 18 19 20 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 22 23 24 25 -
src/ ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/...
│ │ └── products.store.ts │ └── presentational/ ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/... │ │ └── products.store.ts │ └── presentational/ src/ 1 2 3 4 5 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 7 8 9 10 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 12 13 14 15 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 17 18 19 20 21 └── shared/ 22 └── store/ 23 ├── global-checkout.store.ts 24 └── global-products.store.ts 25 └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts src/ 1 ├── products/ 2 │ ├── container/ 3 │ │ ├── products.component.ts/html/... 4 │ │ └── products.store.ts 5 │ └── presentational/ 6 ├── product-details/ 7 │ ├── container/ 8 │ │ ├── product-detail.component.ts/html/... 9 │ │ └── product-detail.store.ts 10 │ └── presentational/ 11 ├── checkout/ 12 │ ├── container/ 13 │ │ ├── checkout.component.ts/html/... 14 │ │ └── checkout.store.ts 15 │ └── presentational/ 16 ├── shell/ 17 │ ├── container/ 18 │ │ ├── shell.component.ts/html/... 19 │ │ └── shell.store.ts 20 │ └── presentational/ 21 22 23 24 25 src/ ├── products/ │ ├── container/ │ │ ├── products.component.ts/html/... │ │ └── products.store.ts │ └── presentational/ ├── product-details/ │ ├── container/ │ │ ├── product-detail.component.ts/html/... │ │ └── product-detail.store.ts │ └── presentational/ ├── checkout/ │ ├── container/ │ │ ├── checkout.component.ts/html/... │ │ └── checkout.store.ts │ └── presentational/ ├── shell/ │ ├── container/ │ │ ├── shell.component.ts/html/... │ │ └── shell.store.ts │ └── presentational/ └── shared/ └── store/ ├── global-checkout.store.ts └── global-products.store.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25