Skip to Content
NgStoreReferenceNgStoreNgStore Class Abstract

NgStore Class Abstract

The NgStore class is a foundational component for implementing reactive state management and handling asynchronous data flow in modules or entire applications. It serves as a central hub, organizing and managing store items like emitters, states, and groups, ensuring seamless interaction among them.

Stores can also include methods to trigger specific actions, making them a powerful and flexible tool for coordinating complex application logic. Their structured design simplifies the development of scalable, maintainable, and reactive applications, ensuring consistency and clarity in managing state and data flow.

By implementing the StoreHooks interface, the store provides lifecycle hooks for executing custom logic before and after key events, such as store initialization and completion.

The NgStore class extends Store and serves as an abstract foundation designed to serve as a base for specific store implementations in Angular applications that define concrete collections of store items

API

abstract class NgStore extends Store { initialize(beforeInit?: (store: this) => void): this; complete(): void; unregisterOnDestroy(): this; protected markAsReady(): void; }

Example

products.store.ts
import {computed, Injectable} from '@angular/core'; import {switchMap} from 'rxjs'; import {emitter, transmit} from '@bitfiber/rx'; import {NgStore, asyncSignalGroup, routeFiltersGroup, signalState} from '@bitfiber/ng/rx'; interface Product { id: number; name: string; price: number; } interface ProductCategory { id: number; name: string; } interface ProductsFilters { search: string; page: number; } interface ProductsState { categories: ProductCategory[]; products: Product[]; } @Injectable() class ProductsStore extends NgStore { // Provides the start of the first data loading process start = emitter<void>(); // Provides the filters state that is synchronized with the route and the filters form routeFilters = routeFiltersGroup<ProductsFilters>({ initialQueryParams: {search: '', page: 1}, }, ({filters}) => { filters.useLazyEmission(); }) // Provides an async group for managing categories loading process categoriesReq = asyncSignalGroup<void, ProductCategory[], Error>((categoriesReq, {launch}) => { launch // Triggers categories loading once the start emitter emits .wait(this.start) // Defines a side effect for loading categories .effect(switchMap(() => categoriesService.get().pipe(transmit(categoriesReq)))); }, []); // Provides an async group for managing products loading process productsReq = asyncSignalGroup<ProductsFilters, Product[], Error>((productsReq, {launch}) => { launch // Triggers products loading after categories are successfully loaded .wait(this.categoriesReq.success, () => this.routeFilters.filters()) // Reloads products when filters are updated .receive(this.routeFilters.filters) // Defines a side effect for loading products .effect(switchMap(filters => productsService.get(filters).pipe(transmit(productsReq)))); }, []); // Provides the loading status state isLoading = computed(() => this.categoriesReq.state().inProgress || this.productsReq.state().inProgress); // Provides the main store state data = signalState<ProductsState>({categories: [], products: []}, s => s // Combines data from categories and products into a single state .select( this.categoriesReq.success, this.productsReq.success, (categories, products) => ({categories, products}), ), ); // Provides the store error handling errors = emitter<Error>(e => e // Collects errors from asynchronous actions .receive(this.categoriesReq.fail, this.productsReq.fail) // Handle errors with a side effect .tap(error => console.log('Error:', error))); // Marks the store as ready, indicating that all store items have been defined #ready = this.markAsReady(); // Automatically starts the store after initialization afterStoreInit(): void { this.start.emit(); } }
products.component.ts
import {Component, inject} from '@angular/core'; import {ReactiveFormsModule} from '@angular/forms'; @Component({ selector: 'bf-products', standalone: true, templateUrl: './products.component.html', imports: [ReactiveFormsModule], providers: [ProductsStore], }) export class ProductsComponent { readonly store = inject(ProductsStore).initialize(); readonly form = this.store.routeFilters.form; }
products.component.html
@if (!store.isLoading()) { <div class="bf-filters" [formGroup]="form" > <input formControlName="search"/> <pagenator formControlName="page"/> </div> @for (product of store.data().products; track product.id) { <div class="bf-product"> {{product.name}} - {{product.price}} </div> } } @else { Data is loading... }
Last updated on