AngularAtlased. 22.2

Cap. 14Reactivitate avansată63 / 75

NgRx SignalStore

State management la scară pe semnale: withState, withComputed, withMethods, rxMethod, entități și hooks — și când un store simplu e suficient.

arhitectură ~0 min de citit

NgRx SignalStore (pachetul @ngrx/signals) e un mod declarativ de a construi store-uri pe semnale. Compui funcționalități (withState, withComputed, withMethods, withHooks, withEntities…), iar rezultatul e o clasă injectabilă. Starea ei e expusă ca semnale și poate fi modificată doar prin metodele pe care le definești tu.

E pasul următor după store-ul „de mână” de pe pagina State management: aceleași idei, dar cu structură standard, entități gata făcute, integrare RxJS pentru fluxuri asincrone și bucăți refolosibile între store-uri. Versiunea folosită aici e @ngrx/signals 22, compatibilă cu Angular 22.

De ce contează
Pe măsură ce aplicația crește, store-urile scrise de mână ajung să arate fiecare altfel: unele expun semnale scriibile, altele nu; unele țin loading/error, altele nu. SignalStore îți dă o singură formă pentru toate. Iar bucățile repetitive (starea unei cereri, paginarea, colecțiile de entități) le scrii o singură dată, ca features.

Anatomia unui store

Un signalStore(...) e o listă de features aplicate în ordine. Fiecare feature vede ce au adăugat cele de dinaintea lui. withState definește starea: fiecare proprietate devine un semnal (store.todos()), iar obiectele imbricate devin deep signals. withComputed adaugă stare derivată, withMethods adaugă operațiile. Doar metodele pot apela patchState. Starea e protejată implicit, deci din afara store-ului nu o poți modifica direct.

import { computed } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withState } from '@ngrx/signals';

type Filter = 'all' | 'open' | 'done';
interface Todo { id: number; title: string; done: boolean }

export const TodoStore = signalStore(
  { providedIn: 'root' }, // fără opțiune: îl pui în `providers` unde ai nevoie
  withState({ todos: [] as Todo[], filter: 'all' as Filter }),
  withComputed(({ todos, filter }) => ({
    visible: computed(() =>
      filter() === 'all' ? todos() : todos().filter((t) => t.done === (filter() === 'done')),
    ),
    openCount: computed(() => todos().filter((t) => !t.done).length),
  })),
  withMethods((store) => ({
    add(title: string): void {
      patchState(store, (s) => ({ todos: [...s.todos, { id: Date.now(), title, done: false }] }));
    },
    toggle(id: number): void {
      patchState(store, (s) => ({ todos: s.todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) }));
    },
    setFilter(filter: Filter): void {
      patchState(store, { filter });
    },
  })),
);

patchState primește fie un obiect parțial ({ filter }), fie o funcție (state) => Partial<State>. Primește și oricâte updatere, aplicate împreună. Starea e imutabilă: întorci mereu obiecte și array-uri noi, altfel semnalele nu observă schimbarea (aceeași regulă ca la signal()).

Unde trăiește store-ul

Ca orice serviciu, durata de viață a store-ului e dată de injectorul în care îl pui. Cu { providedIn: 'root' } ai o singură instanță globală, bună pentru sesiunea utilizatorului sau un coș de cumpărături. Fără opțiune, îl pui în providers-ul unei componente sau al unei rute și primești o instanță nouă, distrusă odată cu ea. E alegerea potrivită pentru starea unei pagini.

// 1. Global: o singură instanță pentru toată aplicația
export const SessionStore = signalStore({ providedIn: 'root' }, withState({ user: null as User | null }));

// 2. Per pagină / per componentă: instanță nouă, distrusă odată cu componenta
export const ProductsStore = signalStore(withEntities<Product>() /* ... */);

@Component({
  providers: [ProductsStore], // fiecare <app-catalog> are store-ul lui
  template: `...`,
})
export class Catalog {
  readonly store = inject(ProductsStore);
}

// 3. Per rută (feature): trăiește cât trăiește ruta
{ path: 'catalog', providers: [ProductsStore], loadComponent: () => import('./catalog') }
LiveStore local vs store în root
Instanța 7
local: 0
root: 0
Instanța 8
local: 0
root: 0
„local” moare odată cu componenta; „root” supraviețuiește.
Apasă „local +1” în fiecare card: fiecare are instanța lui. „root +1” crește același contor în ambele. Distruge cardurile și creează-le din nou: cel local o ia de la zero, cel din root își păstrează valoarea.

Entități

withEntities<Product>() adaugă o colecție normalizată: în stare ai ids și entityMap, iar ca semnal ai entities(), un array derivat. Updaterele din @ngrx/signals/entities (setAllEntities, addEntity, updateEntity, upsertEntity, removeEntity…) se dau direct lui patchState. Pentru mai multe colecții în același store există colecții cu nume (withEntities({ entity: type<Product>(), collection: 'product' }) → productEntities()).

Fluxuri asincrone cu rxMethod

rxMethod (din @ngrx/signals/rxjs-interop) transformă un pipe RxJS într-o metodă. O poți apela cu o valoare, cu un Observable sau cu un semnal. Cu semnal, metoda rulează la fiecare schimbare a lui. Apelul store.search(store.query) din onInit leagă căutarea de query o singură dată. Restul e RxJS obișnuit: debounceTime, switchMap ca să anulezi cererea veche, catchError în interiorul lui switchMap, ca o eroare să nu oprească fluxul (vezi RxJS avansat). Subscripția se închide singură când e distrus store-ul.

import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { patchState, signalStore, withHooks, withMethods, withState } from '@ngrx/signals';
import { setAllEntities, updateEntity, withEntities } from '@ngrx/signals/entities';
import { rxMethod } from '@ngrx/signals/rxjs-interop';
import { catchError, debounceTime, distinctUntilChanged, EMPTY, pipe, switchMap, tap } from 'rxjs';

export const ProductsStore = signalStore(
  withEntities<Product>(),                 // stare: ids + entityMap; semnal: entities()
  withState({ query: '' }),
  withRequestStatus(),                     // feature custom (mai jos)
  withMethods((store, http = inject(HttpClient)) => ({
    setQuery: (query: string) => patchState(store, { query }),
    buy: (id: number) =>
      patchState(store, updateEntity({ id, changes: (p) => ({ stock: p.stock - 1 }) })),

    search: rxMethod<string>(
      pipe(
        debounceTime(300),
        distinctUntilChanged(),
        tap(() => patchState(store, setPending())),
        switchMap((q) =>
          http.get<Page<Product>>(`/api/products?q=${encodeURIComponent(q)}`).pipe(
            tap((page) => patchState(store, setAllEntities(page.items), setFulfilled())),
            catchError(() => {
              patchState(store, setError('Serverul nu răspunde.'));
              return EMPTY; // stream-ul exterior rămâne viu
            }),
          ),
        ),
      ),
    ),
  })),
  withHooks({
    onInit(store) {
      store.search(store.query); // rxMethod primește un SEMNAL: rulează la fiecare schimbare
    },
  }),
);
tapResponse
În documentația NgRx vei vedea des tapResponse în loc de tap + catchError. Face același lucru mai compact, dar vine din pachetul separat @ngrx/operators. Demo-ul de aici folosește doar RxJS.

Features refolosibile

signalStoreFeature(...) grupează mai multe features într-o funcție pe care o adaugi în orice store, de exemplu withRequestStatus(): starea cererii plus isPending și error. Updaterele sunt funcții simple care întorc o bucată de stare, deci le combini cu altele în același patchState. Așa elimini copy-paste-ul de loading/error din fiecare store.

import { computed } from '@angular/core';
import { signalStoreFeature, withComputed, withState } from '@ngrx/signals';

export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string };

// O bucată de store refolosibilă: stare + computed-uri, compusă în orice signalStore
export function withRequestStatus() {
  return signalStoreFeature(
    withState<{ requestStatus: RequestStatus }>({ requestStatus: 'idle' }),
    withComputed(({ requestStatus }) => ({
      isPending: computed(() => requestStatus() === 'pending'),
      error: computed(() => {
        const s = requestStatus();
        return typeof s === 'object' ? s.error : null;
      }),
    })),
  );
}

// Updatere: funcții simple care întorc o parte din stare → patchState(store, setPending())
export const setPending = () => ({ requestStatus: 'pending' as RequestStatus });
export const setFulfilled = () => ({ requestStatus: 'fulfilled' as RequestStatus });
export const setError = (error: string) => ({ requestStatus: { error } as RequestStatus });
LiveStore de produse: entități + rxMethod + feature custom
  • Niciun produs.
getState(store) — live
{
  "query": "",
  "inStockOnly": false,
  "cart": 0,
  "requestStatus": "idle",
  "ids": []
}
În coș: 0 · valoare stoc: 0 lei
Scrie în căutare: cererea pleacă abia după 300 ms de pauză. „Cumpără” modifică o singură entitate cu updateEntity. În dreapta vezi getState(store) actualizat live.

Restul cutiei de unelte

signalState e varianta „fără store”: un obiect de semnale pentru starea locală a unei componente, tot cu patchState. withProps adaugă proprietăți care nu sunt stare (servicii, resurse, observable-uri). withLinkedState face pentru store ce face linkedSignal pentru o componentă. getState îți dă un instantaneu al stării, iar watchState te anunță sincron la fiecare schimbare. Pentru teste, unprotected(store) din @ngrx/signals/testing îți permite să pui store-ul într-o stare anume. Pachetul are și un plugin de evenimente (@ngrx/signals/events), pentru stilul Redux cu evenimente și reducere.

import { patchState, signalState } from '@ngrx/signals';

// Pentru starea locală a UNEI componente: fără store, doar un obiect de semnale
@Component({ /* ... */ })
export class Filters {
  readonly state = signalState({ query: '', page: 1, sort: 'name' as 'name' | 'price' });

  next(): void {
    patchState(this.state, (s) => ({ page: s.page + 1 }));
  }
  // în template: state.query(), state.page()
}

Când DA, când NU

Folosește SignalStore
  • Stare de feature folosită de mai multe componente, cu operații asincrone (căutare, salvare, paginare).
  • Colecții de entități actualizate des (liste editabile, coșuri, tabele).
  • Echipe mai multe care vor aceeași formă pentru toate store-urile și features refolosibile.
  • Ai NgRx Store clasic și vrei ceva mai ușor pentru feature-urile noi. Pot conviețui.
Ajunge ceva mai simplu
  • Starea unei singure componente: signal() sau signalState.
  • Date citite de la server și doar afișate: httpResource (vezi resource).
  • Un serviciu mic cu două semnale și trei metode: un store de mână e mai clar pentru toată lumea.
  • Aplicații în care nimeni din echipă nu cunoaște NgRx și termenul e mâine. Dependența are un cost de învățare.
Store de mânăSignalStoreNgRx Store (Redux)
Boilerplateminimmicmare (actions, reducers, effects, selectors)
Structură standardce decide echipadada, foarte strictă
Entitățiscrii singurwithEntities@ngrx/entity
Asyncresource / RxJSrxMethod, resourceeffects
Bun pentrufeature-uri micimajoritatea aplicațiilorfluxuri complexe, audit de evenimente

Capcane frecvente

  • Mutarea stării (s.todos.push(x) în updater). Semnalul primește aceeași referință și nimic nu se actualizează. Întoarce mereu array-uri și obiecte noi.
  • providedIn: 'root' pentru starea unei pagini. Starea „rămâne” de la o vizită la alta și se amestecă între tab-uri de componente. Pune store-ul în providers-ul paginii.
  • catchError pus după switchMap în rxMethod. Prima eroare închide fluxul și căutarea nu mai funcționează. Pune-l în fluxul interior.
  • Logică de business în componente, cu store-ul folosit doar ca „sac de date”. Metodele store-ului sunt locul ei.
  • Un store uriaș pentru toată aplicația. Fă store-uri mici, pe feature, și compune-le cu features. Așa e mai ușor de testat și de încărcat lazy.
  • effect() în componente ca să sincronizezi store-uri între ele. Preferă withComputed sau withLinkedState (vezi effect()).

Fișă rapidă

signalStore(config?, ...features)        config: { providedIn: 'root' } sau nimic (→ providers: [Store])
withState({ ... })                       stare → semnale: store.x()
withComputed(store => ({ y: computed(...) }))
withMethods((store, dep = inject(X)) => ({ ... }))   singurul loc din care modifici starea
withHooks({ onInit(store) {}, onDestroy(store) {} })
withProps / withLinkedState              proprietăți non-stare / stare legată (ca linkedSignal)
signalStoreFeature(...features)          bucăți refolosibile: withRequestStatus(), withPagination()
patchState(store, partial | updater, ...) mai multe updatere într-un singur apel
getState(store) / watchState(store, fn)   instantaneu / observator sincron al stării
rxMethod<T>(pipe(...))                   din @ngrx/signals/rxjs-interop — primește valoare, semnal sau Observable
withEntities<T>() + setAllEntities, addEntity, updateEntity, removeEntity, upsertEntity  (@ngrx/signals/entities)
signalState({ ... })                     stare locală într-o componentă, fără store
unprotected(store)                       din @ngrx/signals/testing — setezi starea direct în teste

Cap. 14 · 0 din 4 terminate

Ai terminat lecția?

Marcheaz-o și apare pe harta ta: în cuprins și pe prima pagină.

Se marchează singură după ce citești toată lecția.

  1. Semnalele pe dinăuntru
  2. RxJS avansat: operatori de ordin superior
  3. NgRx SignalStore
  4. Mașini de stări cu signals
Vezi toată harta ta

Toate cele 75 de pagini · scrie ca să filtrezi

  • 01Ce e nou în Angular 21 & 22Toate schimbările importante din v21 și v22, pe scurt, cu link către pagina care le explică.Start
  • 01Prima ta aplicație, pas cu pasO listă de lecturi construită de la o componentă goală la o aplicație terminată — signals, @for, input-uri și output-uri, un serviciu și un formular, pas cu pas.Start
  • 01Decizii rapide„Unde pun asta?”, „Signal sau Observable?”, „Shared sau feature?” — răspunsuri scurte la întrebările de zi cu zi.Start
  • 01Întrebări de interviu Angular60 de întrebări reale de interviu pentru junior, mid și senior, cu răspunsul de dat, întrebarea care urmează și greșelile de evitat. Plus o simulare de interviu.Start
  • 02Standalone & bootstrapCum pornește o aplicație fără NgModule: bootstrapApplication, app.config.ts, imports în componentă.Fundamente moderne
  • 02Data binding și evenimenteInterpolare, binding pe proprietăți și pe atribute, evenimente cu $event și filtre de taste și cum e construit [( )] — ce intră în DOM și ce se întoarce.Fundamente moderne
  • 02Control flow: @if, @for, @switchSintaxa de blocuri care înlocuiește *ngIf/*ngFor/ngSwitch, cu track, @empty și verificare exhaustivă.Fundamente moderne
  • 02@let și expresiile noi din templateVariabile locale cu @let, arrow functions, spread, regex, instanceof, comentarii în tag-uri.Fundamente moderne
  • 02@defer — lazy loading în templateÎncarci componente grele doar când e nevoie: triggere, prefetch, placeholder, loading, error.Fundamente moderne
  • 02Class & style bindingsDe ce [class.x] și [style.x] au înlocuit ngClass și ngStyle (cu migrare automată din v21).Fundamente moderne
  • 02Stilurile componentelor și încapsularea:host, cele trei moduri de încapsulare, teme cu variabile CSS și de ce ::ng-deep e ultima soluție.Fundamente moderne
  • 02Pipes: când și cumPipe pur vs impur, pipe custom și cum alegi între pipe, computed() și o metodă.Fundamente moderne
  • 03signal() și computed()Primitivele reactive: stare scriibilă, stare derivată, egalitate, untracked și reguli de imutabilitate.Signals & reactivitate
  • 03effect() — când DA, când NUEfecte secundare sincronizate cu semnale: localStorage, logging, API-uri imperative. Și capcanele lor.Signals & reactivitate
  • 03linkedSignal() — stare derivată editabilăUn semnal care se resetează când se schimbă sursa, dar pe care îl poți și seta manual.Signals & reactivitate
  • 03resource, rxResource, httpResourceDate asincrone ca semnale: status, value, error, reload, anulare automată. Stabile din v22.Signals & reactivitate
  • 03debounced() — semnale cu întârziereVarianta „debounce” a unui semnal, fără RxJS. Perfect pentru căutare.Signals & reactivitate
  • 03Liste: căutare, filtrare, sortare, paginareUn lanț de computed-uri care recalculează doar ce s-a schimbat, cu filtrele ținute în URL, ca un link să redeschidă aceeași vedere.Signals & reactivitate
  • 03Observables de la zeroCe e un Observable, subscribe și unsubscribe, operatorii pe care îi folosești în fiecare săptămână, cold vs hot — și când ajunge un signal.Signals & reactivitate
  • 03Signals ↔ RxJStoSignal, toObservable, takeUntilDestroyed, outputFromObservable — și când RxJS rămâne alegerea bună.Signals & reactivitate
  • 04input() și output()Inputuri ca semnale (required, transform, alias) și outputuri tipizate. Data down, events up.Componente: comunicare
  • 04model() — two-way bindingBanana-in-a-box [( )] cu un singur API. Când îl folosești și când e mai bine input + output.Componente: comunicare
  • 04viewChild & contentChildInterogări ca semnale: viewChild, viewChildren, contentChild, contentChildren, opțiunea read.Componente: comunicare
  • 04Lifecycle & DestroyRefCe hook-uri mai contează în era signals, afterNextRender, DestroyRef și ngOnChanges tipizat.Componente: comunicare
  • 04Content projectionng-content cu select, sloturi multiple, conținut implicit și capcana proiecției condiționale.Componente: comunicare
  • 04ng-template & ngTemplateOutletTemplate-uri ca valori: render props în Angular, context tipizat și personalizare de componente.Componente: comunicare
  • 05Smart vs DumbContainer vs presentational: cine ține starea, cine doar afișează, și cum le împarți corect.Design de componente
  • 05Componente generice <T>Un DataTable<T> sau Select<T> tipizat corect, cu template-uri pentru celule.Design de componente
  • 05Componente dinamiceNgComponentOutlet, createComponent cu inputBinding/outputBinding și un dashboard construit din config.Design de componente
  • 05Directive & host compositionDirective atribut, metadata host: {} și Directive Composition API (hostDirectives).Design de componente
  • 05Controale custom de formularFormValueControl (Signal Forms) vs ControlValueAccessor: un rating cu stele, în ambele variante.Design de componente
  • 05Change detectionOnPush e implicit din v22, zone.js lipsește din v21. Ce declanșează randarea și cum să nu te arzi.Design de componente
  • 06inject() & @Service()Noul decorator @Service() din v22, inject() vs constructor și contextul de injecție.Dependency Injection
  • 06Providers & ierarhia de injectoriRoot vs rută vs componentă, useClass/useValue/useFactory/useExisting și modificatorii optional/self/skipSelf.Dependency Injection
  • 06InjectionToken & provideX()Configurare tipizată, funcții provideFeature(), makeEnvironmentProviders și inițializatori.Dependency Injection
  • 06injectAsync() — servicii lazyÎncarci un serviciu greu abia când ai nevoie de el, cu prefetch la idle.Dependency Injection
  • 07Structura de folderecore / shared / features, ce intră în fiecare și cum arată un feature pe dinăuntru.Arhitectura aplicației
  • 07Shared vs featureCând muți o componentă în shared și (mai ales) când NU. Checklist concret.Arhitectura aplicației
  • 07Interfețe & modeleInterfață inline sau fișier separat? DTO vs model de domeniu, type vs interface, unde le pui.Arhitectura aplicației
  • 07State management cu signalsStore-uri simple pe semnale, facade, stare locală vs globală și când îți trebuie NgRx.Arhitectura aplicației
  • 07Style guide & namingConvențiile oficiale actuale: nume de fișiere fără sufixe, protected, readonly, ordinea membrilor.Arhitectura aplicației
  • 07Barrels, aliasuri & granițeindex.ts, path aliases, API public pe feature și reguli de dependență care se pot verifica automat.Arhitectura aplicației
  • 07Erori: @boundary & ErrorHandlerNoul bloc @boundary din v22.2, ErrorHandler global, erori HTTP și erori din resource.Arhitectura aplicației
  • 08Routing modern & lazy loadingloadComponent, rute copil, parametri ca input-uri și moștenirea parametrilor (default nou în v22).Routing
  • 08Guards & resolverscanActivate, canMatch, canDeactivate și resolve ca funcții, cu redirect prin UrlTree / RedirectCommand.Routing
  • 09Signal Forms — bazeleFormulare construite pe semnale: form(), [formField], validare prin schemă. Stabile din v22.Formulare
  • 09Signal Forms — avansatValidare custom și cross-field, async/HTTP cu debounce, liste dinamice, disabled/hidden, scheme reutilizabile.Formulare
  • 09Reactive Forms tipizateFormGroup tipizat, nonNullable, FormArray — și cum conviețuiesc cu Signal Forms.Formulare
  • 10HttpClient, interceptors & API layerHttpClient fără setup (v21), Fetch implicit (v22), interceptori funcționali și stratul de API cu mapare DTO.HTTP & date
  • 11Performanță — checklistCe chiar contează: OnPush + signals, track, @defer, lazy routes, imagini, bundle și hidratare.Calitate & performanță
  • 11Testare cu VitestVitest e runner-ul implicit din v21. TestBed, componente cu semnale, servicii, HTTP și harness-uri.Calitate & performanță
  • 11DevTools: cum te uiți în aplicațieChrome DevTools și Angular DevTools pas cu pas: ce arată fiecare panou, cu exerciții care merg chiar pe site-ul ăsta.Calitate & performanță
  • 11Angular AriaComponente headless, accesibile, pe care le stilizezi cum vrei: tabs, accordion, menu, listbox...Calitate & performanță
  • 12SSR, prerender & render modesCând randezi pe server, când la build și când doar în browser: RenderMode pe rută, app.routes.server.ts și costurile reale.SSR & hidratare
  • 12Hidratare: event replay & incrementalCum preia browserul HTML-ul de pe server fără să-l refacă: hidratare completă, event replay, @defer (hydrate on …) și erorile de nepotrivire.SSR & hidratare
  • 12Cod compatibil cu serverulwindow nu există pe server: PLATFORM_ID, afterNextRender, DOCUMENT, TransferState, cache-ul HTTP și PendingTasks.SSR & hidratare
  • 13Micro frontendsCând merită să spargi o aplicație în mai multe deploy-uri și când e doar complexitate: Native Federation, Module Federation, iframes, Web Components.Arhitectură la scară
  • 13Web Components cu Angular ElementscreateCustomElement: o componentă Angular folosită ca tag HTML oriunde, plus comunicarea între aplicații prin evenimente și BroadcastChannel.Arhitectură la scară
  • 13Monorepo, librării & granițeUn workspace cu mai multe aplicații și librării: ng generate library, Nx, entry points secundare, publicare și reguli de dependență.Arhitectură la scară
  • 13Arhitectură hexagonală (ports & adapters)Domeniul fără Angular, porturi ca clase abstracte și adaptoare interschimbabile: HTTP, localStorage sau in-memory, fără să atingi UI-ul.Arhitectură la scară
  • 14Semnalele pe dinăuntruGraful reactiv: producători și consumatori, push-pull, de ce computed e lazy și glitch-free, problema diamantului și când rulează efectele.Reactivitate avansată
  • 14RxJS avansat: operatori de ordin superiorswitchMap, mergeMap, concatMap și exhaustMap pe o diagramă live, plus retry cu backoff, shareReplay și cum eviți scurgerile.Reactivitate avansată
  • 14NgRx SignalStoreState management la scară pe semnale: withState, withComputed, withMethods, rxMethod, entități și hooks — și când un store simplu e suficient.Reactivitate avansată
  • 14Mașini de stări cu signalsFără stări imposibile: o mașină tipizată pe signals, cu tranziții, guards și diagrama stărilor, live.Reactivitate avansată
  • 15WebSockets: timp real care rezistă rețeleiUn socket tipizat ca signals: reconectare cu backoff și jitter, heartbeat, coadă de mesaje cât ești offline și siguranță la SSR.Timp real și offline
  • 15Actualizări optimiste și rollbackInterfață instantanee, dar onestă: snapshot și rollback, răspunsuri sosite în altă ordine, chei de idempotență.Timp real și offline
  • 15Offline-first: Service Worker și cacheService Worker-ul Angular: strategii de cache, anunțarea versiunilor noi și o coadă pentru modificările făcute offline.Timp real și offline
  • 16Web WorkersCalcule grele fără să blochezi interfața: un worker real în pagină, comunicare prin mesaje și când nu merită.UI & performanță avansată
  • 16Liste uriașe: virtual scroll100.000 de rânduri fără lag: CDK virtual scroll, itemSize, trackBy și alternativele (paginare, infinite scroll).UI & performanță avansată
  • 16Animații moderne & View Transitionsanimate.enter și animate.leave în locul @angular/animations (depreciat), tranziții CSS și View Transitions la navigare.UI & performanță avansată
  • 16Memory leaks: cum le găsești și le repariSubscripții, timere și listeneri care trăiesc mai mult decât componenta lor — cum îi vezi în DevTools și cum îi închizi.UI & performanță avansată
  • 17Securitate: XSS, sanitizare & CSPCe face Angular automat împotriva XSS, când devine periculos bypassSecurityTrust, plus CSP cu nonce, Trusted Types și XSRF.Securitate & globalizare
  • 17Autentificare: token-uri & refreshUn interceptor care atașează token-ul, reîmprospătează o singură dată la 401 și reia cererile din coadă — plus unde ții token-ul.Securitate & globalizare
  • 17OAuth 2.1 și OpenID Connect cu PKCEFluxul authorization code cu PKCE, pas cu pas: verifier și challenge, state și nonce, ID token-ul și de ce un BFF e mai sigur.Securitate & globalizare
  • 17Internaționalizare (i18n)$localize, atributul i18n, ICU plural/select, build per limbă vs. traduceri la runtime și formatarea după locale.Securitate & globalizare