12

Lab 12 - Angular Frontend (Part 2) - CRUD, Reactive Forms, Role-based UI

Partea 4 din 5

Actualizat 2026-05-17Sursă pe GitHub ↗Cod start

Parte 4 — Glossary: C# / ASP.NET Core → Angular

Mapare concept-cu-concept între ASP.NET Core MVC (Lab 1-10) și Angular (Lab 11-12). Multe lucruri din Angular au echivalent direct în ce știți deja — această secțiune face „puntea" ca să nu învățați conceptele de la zero.

Mapping conceptual

În ASP.NET Core / Razor știți În Angular se cheamă Notă
class FooController : Controller @Component({...}) export class Foo clasa cu metadata via decorator
[Route("/articles")] pe controller { path: 'articles', component: Foo } în app.routes.ts routing declarativ, dar pe client
services.AddScoped<IArticleService, ArticleService>() în Program.cs @Injectable({ providedIn: 'root' }) pe clasă DI singleton, înregistrare implicită
public FooController(IArticleService svc) cu constructor injection private svc = inject(ArticleService) ca field injecție automată, fără constructor
Task<List<Article>> async method Observable<Article[]> din HttpClient stream lazy — pornește la .subscribe(), nu la apel
await _svc.GetAllAsync() this.svc.getAll().subscribe(data => ...) sintaxă diferită, același flow
@if (condition) { ... } în Razor *ngIf="condition" sau @if (condition) { } la fel; *ngIf e clasic, @if e mai nou (v17+)
@foreach (var x in list) { ... } *ngFor="let x of list" sau @for (x of list; track x.id) { } la fel; track în @for e ca un key
@Html.DisplayFor(m => m.Date) cu formatter {{ date | date:'dd MMM yyyy' }} (pipe) pipe = transformator pe template, înlănțuibile (| filter1 | filter2)
[Required, MinLength(5)] pe model property Validators.required, Validators.minLength(5) în FormBuilder aceeași listă de validatori built-in
if (ModelState.IsValid) { ... } if (this.form.valid) { ... } același concept, syntax diferit
<form asp-action="Create"> + [HttpPost] action <form [formGroup]="form" (ngSubmit)="onSubmit()"> submit handler explicit, FormGroup binding
<input asp-for="Email" /> <input formControlName="email" /> binding la control specific
<span asp-validation-for="Email"> <div *ngIf="form.get('email')?.errors"> mesaj de eroare per câmp
ViewBag.Title = "..." sau Layout = "_Layout" proprietate pe component (signal) template direct, fără bag de runtime
appsettings.json injectat prin IConfiguration environment.ts / environment.development.ts build replacement, nu read la runtime
Middleware app.UseCors() authInterceptor: HttpInterceptorFn în app.config.ts similar concept, dar pe HTTP client (browser), nu server
[Authorize] pe action sau controller canActivate: [authGuard] pe rută guard = decide înainte de a randa componenta
[Authorize(Roles = "Admin")] logică manuală în canModify(article) cu currentUser().roles.includes('Admin') nu există decorator pe metode în Angular standard
_logger.LogInformation("...") console.log("...") (pentru lab; producție: Sentry, etc.) nu există infrastructură de logging built-in
IConfiguration["ApiKey"] environment.apiKey static la build, nu dinamic
_Layout.cshtml cu @RenderBody() app.html cu <router-outlet /> shell-ul aplicației, ține header/footer comun
Url.Action("Edit", "Articles", new { id = 5 }) routerLink="/articles/5/edit" sau router.navigate(['/articles', 5, 'edit']) navigare declarativă sau programatică
RedirectToAction("Index") din action this.router.navigate(['/articles']) navigare după success
partial views (@Html.Partial(...)) sub-componente injectate cu <app-x /> reuse de UI

Conceptul nou — Signals (nu ai echivalent direct în C#)

signal() e singurul concept Angular care nu există în ASP.NET Core standard. Cel mai apropiat echivalent: INotifyPropertyChanged din WPF/MAUI/desktop XAML — „o proprietate care anunță automat când se schimbă, ca UI-ul să se actualizeze". Dar Angular face mult mai puțină ceremonie:

// Creezi:
count = signal(0);
items = signal<string[]>([]);

// Citești (cu paranteze — e o funcție):
this.count();           // returnează valoarea curentă
const len = this.items().length;

// Scrii (notifică automat consumatorii):
this.count.set(5);
this.count.update(v => v + 1);
this.items.update(arr => [...arr, 'nou']);

În template:

<p>Count: {{ count() }}</p>
<div *ngIf="count() > 0">Avem date</div>

Când scrii count() în template, Angular reține: „acest binding depinde de signal-ul count; când se schimbă, re-randez doar partea asta". Identic conceptual cu OnPropertyChanged din WPF, doar că Angular îl tracking-uiește automat — nu scrii cod manual.

Echivalent WPF/MAUI mental model:

// WPF cu INotifyPropertyChanged
private int _count;
public int Count
{
    get => _count;
    set { _count = value; OnPropertyChanged(nameof(Count)); }
}

vs Angular:

count = signal(0);   // toata munca de mai sus, intr-o linie

Table of Contents Angular minimal (10 concepte)

Tot ce ai nevoie să înțelegi din Angular pentru lab 11-12, în 10 puncte:

  1. Component — clasă cu template HTML + CSS, decorată cu @Component({...}). Toate sunt standalone (își declară singure imports-urile). E echivalent cu Controller + View combinat într-un singur loc.

  2. Service — clasă injectabilă, singleton la nivel de aplicație. @Injectable({providedIn: 'root'}). Echivalent cu serviciile din Program.cs cu AddScoped.

  3. DI cu inject() — în loc de constructor. private svc = inject(ArticleService) ca field. Same concept, syntax mai concis.

  4. Signals — primitivul de stare reactivă. signal(), .set(), .update(), citire cu (). Nu există în C# standard.

  5. HttpClient — face request-uri, returnează Observable<T>. Echivalent cu HttpClient din .NET, dar întoarce stream (Observable) în loc de Task.

  6. Router — URL → component map, definit în app.routes.ts. RouterLink directive e ca asp-action din MVC.

  7. Reactive Forms (FormBuilder, FormGroup, Validators) — formulare cu validare declarativă în TS. Echivalent cu ModelState + DataAnnotations, dar pe client.

  8. Pipes (| date, | async, | slice, | uppercase) — transformări în template. Echivalent cu format providers / display templates din Razor.

  9. Interceptors funcționali — middleware pe HTTP client (browser side). Atașează automat Authorization: Bearer <token> la fiecare request, similar conceptual cu middleware-ul din app.UseAuthentication() (dar pe client).

  10. Guards funcționali — middleware pe routing. canActivate: [authGuard] decide dacă utilizatorul are voie să intre pe rută. Echivalent cu [Authorize] dar pe client (UX guard, nu securitate — backend-ul tot verifică).

Atât. Restul sunt convenții de naming, folder structure, RxJS operators (instrumente pentru manipularea Observable-elor). Conceptele de bază sunt finite — sub 10 piese mari, restul e detail.

Ghid de translatare pentru când scrii cod Angular

Când scrii ceva nou, întreabă-te: „În ASP.NET Core cum aș face asta?" Apoi mapează:

  • Am nevoie să afișez ceva pe pagină → component cu template HTML (cu *ngIf/*ngFor ca în Razor)
  • Am nevoie de date de la API → service cu HttpClient, component îl injectează cu inject()
  • Am nevoie de form cu validareFormBuilder în component, Validators.X (la fel ca DataAnnotations), template cu [formGroup]
  • Am nevoie să schimb paginarouter.navigate(['/...']) (programmatic) sau routerLink="/..." (declarativ)
  • Am nevoie să restricționez accesul → guard funcțional pe rută (canActivate: [authGuard])
  • Am nevoie să atașez ceva la fiecare request → interceptor funcțional în app.config.ts
  • Am nevoie de state care se schimbă și UI-ul să se update-ezesignal() în component
  • Am nevoie de configurări per environmentenvironment.ts (prod) vs environment.development.ts (dev)

Această secțiune e dictionar. Cheatsheet-ul de mai jos (Parte 5) are sintaxă API exactă.