State management (service)
Kichik va o'rta ilovalar uchun NgRx'siz service-based state yetarli. BehaviorSubject an'anaviy, signal() esa Angular 17+ da zamonaviy yondashuv.
BehaviorSubject pattern — local state
Nima bu?
BehaviorSubject oxirgi qiymatni saqlaydigan hot Observable. Service ichida private BehaviorSubject, tashqariga faqat asObservable() (readonly) beriladi. Component'lar subscribe qiladi yoki async pipe ishlatadi. Bu RxJS asosidagi sodda store pattern.
Gotcha: async pipe subscribe'ni komponent yo'q qilinganda avtomatik bekor qiladi, lekin komponent ichida qo'lda .subscribe() qilingan bo'lsa, ngOnDestroy'da unsubscribe() qilinmasa memory leak yuzaga keladi — service providedIn: 'root' bo'lgani uchun Subject butun ilova umri davomida yashaydi va subscriber'lar to'planib boraveradi. Zamonaviy yechim: takeUntilDestroyed() operatori yoki iloji boricha async pipe'dan foydalanish.
Kod misoli
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
interface CartItem {
id: number;
name: string;
qty: number;
}
interface CartState {
items: CartItem[];
loading: boolean;
}
const initialState: CartState = { items: [], loading: false };
@Injectable({ providedIn: 'root' })
export class CartStore {
private state$ = new BehaviorSubject<CartState>(initialState);
// Faqat o'qish — tashqaridan next() chaqirish mumkin emas
readonly cart$ = this.state$.asObservable();
readonly total$ = this.cart$.pipe(
map((s) => s.items.reduce((sum, i) => sum + i.qty, 0))
);
addItem(item: CartItem): void {
const current = this.state$.value;
const existing = current.items.find((i) => i.id === item.id);
const items = existing
? current.items.map((i) =>
i.id === item.id ? { ...i, qty: i.qty + item.qty } : i
)
: [...current.items, item];
this.state$.next({ ...current, items });
}
setLoading(loading: boolean): void {
this.state$.next({ ...this.state$.value, loading });
}
clear(): void {
this.state$.next(initialState);
}
}
@Component({
selector: 'app-cart-badge',
standalone: true,
imports: [AsyncPipe],
template: `Savat: {{ total$ | async }}`,
})
export class CartBadgeComponent {
total$ = inject(CartStore).total$;
}
Imtihonda
Savol: Nima uchun BehaviorSubject ni to'g'ridan-to'g'ri expose qilmaslik kerak?
Javob: Tashqi kod .next() chaqirib state'ni buzishi mumkin. asObservable() — faqat o'qish. O'zgartirish faqat service metodlari orqali.
Yodlash uchun
Signal-based store (v17+)
Nima bu?
Angular Signals (signal, computed, update) service ichida reaktiv state boshqarish uchun ishlatiladi. Subscribe/unsubscribe kerak emas — template'da to'g'ridan-to'g'ri store.items() chaqiriladi. computed derived state uchun, effect side-effect uchun.
Kod misoli
import { Injectable, computed, signal } from '@angular/core';
export interface Todo {
id: number;
title: string;
done: boolean;
}
@Injectable({ providedIn: 'root' })
export class TodoStore {
private _todos = signal<Todo[]>([]);
private _filter = signal<'all' | 'active' | 'done'>('all');
readonly todos = this._todos.asReadonly();
readonly filter = this._filter.asReadonly();
readonly filteredTodos = computed(() => {
const list = this._todos();
const f = this._filter();
if (f === 'active') return list.filter((t) => !t.done);
if (f === 'done') return list.filter((t) => t.done);
return list;
});
readonly activeCount = computed(
() => this._todos().filter((t) => !t.done).length
);
addTodo(title: string): void {
this._todos.update((list) => [
...list,
{ id: Date.now(), title, done: false },
]);
}
toggle(id: number): void {
this._todos.update((list) =>
list.map((t) => (t.id === id ? { ...t, done: !t.done } : t))
);
}
setFilter(filter: 'all' | 'active' | 'done'): void {
this._filter.set(filter);
}
}
@Component({
selector: 'app-todo-list',
standalone: true,
template: `
<p>Faol: {{ store.activeCount() }}</p>
@for (todo of store.filteredTodos(); track todo.id) {
<label>
<input type="checkbox" [checked]="todo.done" (change)="store.toggle(todo.id)" />
{{ todo.title }}
</label>
}
`,
})
export class TodoListComponent {
store = inject(TodoStore);
}
Imtihonda
Savol: Signal store va BehaviorSubject — qaysi biri OnPush bilan yaxshiroq ishlaydi?
Javob: Signal — Angular signal-based change detection bilan to'g'ridan-to'g'ri integratsiya. BehaviorSubject + async pipe ham OnPush'da yaxshi, lekin subscribe boilerplate ko'proq.
Yodlash uchun
