La lliçó anterior va exposar el domini de BiblioTech com una API HTTP amb ASP.NET Core, pensada
perquè qualsevol client la consumeixi —inclòs un client escrit en JavaScript, l'opció tradicional
per construir interfícies web—. Blazor proposa una altra via: construir aquesta interfície
web amb C#, reutilitzant el mateix llenguatge i bona part de les idees de components i data
binding ja vistes a WPF, però executant-se en el context d'un navegador. Aquesta lliçó explica
els dos models d'execució de Blazor (Server i WebAssembly), la sintaxi d'un component .razor, i
construeix un component Cataleg.razor que llista el catàleg de BiblioTech i injecta
Biblioteca directament com a servei, sense passar per peticions HTTP manuals.
Contingut
- Què és Blazor: C# també al navegador
- Els dos models: Blazor Server davant Blazor WebAssembly
- Components
.razor: estructura bàsica - Sintaxi de Razor:
@code,@bind,@onclick - Cicle de vida d'un component:
OnInitializedAsync - Injecció de dependències en un component:
@inject - Exemple complet:
Cataleg.razorper a BiblioTech - Quan triar Blazor davant ASP.NET Core "pur" o davant WPF
- Què és Blazor: C# també al navegador
Tradicionalment, la interfície d'una aplicació web es construeix amb HTML, CSS i JavaScript,
mentre que el servidor (per exemple, amb ASP.NET Core, lliçó anterior) es limita a servir dades.
Blazor trenca aquesta frontera: permet escriure la interfície web en C#, organitzada en
components reutilitzables (fitxers .razor), sense necessitat d'escriure JavaScript per a la
lògica de la interfície.
La conseqüència pràctica més important: tot el que ja saps de C# —classes, LINQ, async/await,
el mateix domini de BiblioTech— es pot fer servir directament al navegador, sense traduir-lo a
cap altre llenguatge ni exposar-lo primer com a API JSON, com sí que caldria amb un frontend
tradicional en JavaScript.
- Els dos models: Blazor Server davant Blazor WebAssembly
Blazor ofereix dues formes diferents d'executar aquest mateix codi C#, amb implicacions molt diferents sobre on viu realment la lògica:
| Blazor Server | Blazor WebAssembly (WASM) | |
|---|---|---|
| On s'executa el codi C# | Al servidor; el navegador només rep actualitzacions d'interfície | Directament al navegador, compilat a WebAssembly |
| Comunicació amb el servidor | Connexió persistent (SignalR) per a cada interacció de l'usuari | Cap de necessària després de la càrrega inicial (excepte crides explícites a una API) |
| Latència d'interacció | Depèn de la xarxa: cada clic viatja al servidor i torna | Cap latència de xarxa per a la lògica local, s'executa al propi navegador |
| Mida de descàrrega inicial | Petita (la lògica no es descarrega, es queda al servidor) | Més gran (cal descarregar el runtime de .NET compilat a WebAssembly) |
| Funciona sense connexió després de carregar | No: cada interacció depèn de la connexió amb el servidor | Sí, un cop carregat (si no depèn d'una API externa) |
| Accés directe a recursos del servidor (base de dades, fitxers) | Directe, sense API intermèdia | Requereix una API HTTP, com la de l'apartat 3 de la lliçó anterior |
Per a BiblioTech, Blazor Server encaixa millor amb l'exemple d'aquesta lliçó: el component
Cataleg.razor pot injectar Biblioteca directament com a servei del servidor (igual que un
endpoint d'ASP.NET Core), sense passar per HTTP; Blazor WebAssembly, en canvi, obligaria a
consumir l'API construïda a la lliçó anterior mitjançant HttpClient (Mòdul 5), perquè el codi
C# del component s'executaria al navegador de l'usuari, sense accés directe al servidor ni a la
seva base de dades.
- Components
.razor: estructura bàsica
.razor: estructura bàsicaUn component Blazor combina marcatge de tipus HTML amb codi C#, en un mateix fitxer .razor:
@page "/catalogo"
<h3>Cataleg de BiblioTech</h3>
<ul>
<li>Rayuela</li>
<li>Ficciones</li>
</ul>
@code {
// codi C# del component: propietats, metodes, cicle de vida (apartat 5)
}| Part | Paper |
|---|---|
@page "/catalogo" |
Directiva que assigna una ruta URL a aquest component (com una pàgina independent) |
| Marcatge HTML | L'estructura visual, molt semblant a HTML pla |
@code { ... } |
Bloc de codi C# del component: aquí hi viuen propietats, camps i mètodes |
La barreja d'HTML i C# dins del mateix fitxer recorda la separació XAML/code-behind de WPF, però
amb una diferència notable: a Blazor, el marcatge i el codi C# conviuen en un únic fitxer
.razor, en lloc de repartir-se entre un .xaml i un .xaml.cs separats.
- Sintaxi de Razor:
@code, @bind, @onclick
@code, @bind, @onclickDins del marcatge, qualsevol expressió C# s'introdueix amb @:
Per al data binding d'un camp de formulari, @bind connecta el valor d'un control amb una
propietat C#, d'una manera semblant al {Binding ...} de WPF però amb sintaxi pròpia de Razor:
<input @bind="textCerca" />
<p>Cercant: @textCerca</p>
@code {
private string textCerca = string.Empty;
}Per gestionar esdeveniments, @onclick (i equivalents com @onchange, @onsubmit) connecta un
esdeveniment del DOM amb un mètode C#, sense necessitat de JavaScript:
<button @onclick="IncrementarComptador">Sumar</button>
<p>Comptador: @comptador</p>
@code {
private int comptador = 0;
private void IncrementarComptador()
{
comptador++; // Blazor torna a renderitzar el component automaticament despres de l'esdeveniment
}
}Després de qualsevol esdeveniment gestionat per Blazor (@onclick, @bind...), el framework
torna a renderitzar automàticament la part del component que hagi canviat —d'una manera
conceptualment semblant a com WPF actualitza la vista en disparar PropertyChanged, encara que
el mecanisme intern de Blazor sigui diferent (compara l'arbre de marcatge abans i després de
l'esdeveniment, i actualitza només les diferències).
- Cicle de vida d'un component:
OnInitializedAsync
OnInitializedAsyncUn component Blazor passa per una sèrie de mètodes de cicle de vida, invocats automàticament pel
framework en moments concrets. El més habitual per carregar dades en mostrar el component és
OnInitializedAsync:
@code {
private List<MaterialBibliotecari> cataleg = new();
protected override async Task OnInitializedAsync()
{
// s'executa una vegada, quan el component es mostra per primera vegada
cataleg = await ObtenirCatalegAsync();
}
}OnInitializedAsync és el lloc habitual per carregar dades inicials —d'una base de dades, d'una
API, o, com a l'apartat 7, directament d'un servei injectat—, d'una forma equivalent a com el
constructor de CatalegViewModel a WPF preparava Cataleg abans de mostrar la finestra. La
diferència és que aquí és un mètode async, pensat per esperar operacions que trigin (com una
consulta a base de dades) sense bloquejar la càrrega inicial de la pàgina.
- Injecció de dependències en un component:
@inject
@injectUn component Blazor pot rebre serveis registrats al contenidor de dependències (el mateix
mecanisme vist a la lliçó d'ASP.NET Core) amb la directiva @inject:
@inject Biblioteca Biblioteca resol una instància de Biblioteca des del contenidor de serveis
(registrada a Program.cs amb builder.Services.AddSingleton<Biblioteca>(), igual que a la
lliçó anterior) i l'exposa com una propietat disponible a tot el component, sense necessitat de
rebre-la per paràmetre ni construir-la manualment. Com s'ha indicat a la introducció del mòdul, la
injecció de dependències com a mecanisme general s'estudia en profunditat al Mòdul 8; aquí n'hi ha
prou amb reconèixer que @inject és, per a un component Blazor, l'equivalent a rebre un paràmetre
ja resolt en un endpoint d'ASP.NET Core.
- Exemple complet:
Cataleg.razor per a BiblioTech
Cataleg.razor per a BiblioTechUnint totes les peces anteriors, un component que llista el catàleg i permet prestar cada llibre
amb un botó, fent servir Blazor Server i injectant Biblioteca directament:
@page "/catalogo"
@inject Biblioteca Biblioteca
<h3>Cataleg de BiblioTech</h3>
@if (missatgeEstat is not null)
{
<p><strong>@missatgeEstat</strong></p>
}
<table>
<thead>
<tr>
<th>Titol</th>
<th>Autor</th>
<th>Disponible</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (MaterialBibliotecari material in Biblioteca.Cataleg)
{
<tr>
<td>@material.Titol</td>
<td>@material.Autor</td>
<td>@(material.Disponible ? "Si" : "No")</td>
<td>
<button @onclick="() => PrestarMaterial(material)" disabled="@(!material.Disponible)">
Prestar
</button>
</td>
</tr>
}
</tbody>
</table>
@code {
private string? missatgeEstat;
protected override Task OnInitializedAsync()
{
// El cataleg ja viu en memoria dins de Biblioteca (injectada a dalt);
// aqui no cal carregar res addicional, a diferencia d'un escenari amb API externa.
return Task.CompletedTask;
}
private void PrestarMaterial(MaterialBibliotecari material)
{
if (!material.Disponible)
{
missatgeEstat = $"'{material.Titol}' ja esta prestat.";
return;
}
material.Prestar(); // logica de domini ja existent, Modul 2
missatgeEstat = $"Prestec registrat: {material.Titol}";
}
}@onclick="() => PrestarMaterial(material)" fa servir una expressió lambda (Mòdul 4) per passar
el material concret de cada fila a PrestarMaterial, una cosa necessària perquè @foreach
genera una fila per element i cada botó ha d'actuar sobre el seu propi material, no sobre un
de fix. disabled="@(!material.Disponible)" desactiva el botó quan el material ja està prestat,
sense necessitat de cap codi addicional: Razor avalua l'expressió C# entre parèntesis i l'assigna
a l'atribut HTML disabled. Igual que a WPF, cap fragment d'aquest component reimplementa la
comprovació de disponibilitat: es recolza completament en material.Prestar(), que ja la resol
des del Mòdul 2.
- Quan triar Blazor davant ASP.NET Core "pur" o davant WPF
| Escenari | Opció més adequada |
|---|---|
| API pura, sense interfície visual pròpia, consumida per diferents clients | ASP.NET Core (Minimal APIs, lliçó anterior) |
| Interfície visual accessible des de qualsevol navegador, sense instal·lació | Blazor |
| Interfície d'escriptori amb màxim control visual i sense dependre de connexió de xarxa | WPF |
| Interfície d'escriptori simple, ràpida de construir, per a ús intern a Windows | Windows Forms |
Blazor no substitueix ASP.NET Core: de fet, un projecte Blazor Server s'executa sobre ASP.NET Core (fa servir Kestrel i el mateix contenidor de serveis per sota); la diferència és que Blazor afegeix, a sobre, un model de components amb interfície visual, mentre que la Minimal API de la lliçó anterior es queda només a la capa de dades, sense interfície pròpia.
Errors Comuns i Consells
- Confondre Blazor Server amb Blazor WebAssembly en decidir com accedir a les dades: a Blazor
Server, injectar
Bibliotecadirectament (com a l'apartat 7) és correcte i eficient; a Blazor WebAssembly, el mateix enfocament no funcionaria, perquè el component s'executa al navegador del client, sense accés directe al servidor —caldriaHttpClientcontra l'API de l'apartat anterior. - Oblidar
disabled="@(!material.Disponible)"o una comprovació equivalent: sense ella, l'usuari podria fer clic a "Prestar" sobre un material ja prestat; el mètodePrestarMaterialho detecta igualment (per la comprovació deDisponible), però desactivar el botó millora l'experiència evitant l'intent en primer lloc. - Escriure lògica de negoci directament al marcatge Razor en lloc de en un mètode del bloc
@code: dificulta la lectura i la prova del component; la regla pràctica és la mateixa que a WPF, mantenir el marcatge declaratiu i la lògica al codi. - Consell: Blazor Server manté una connexió persistent (SignalR) amb cada usuari connectat; en una aplicació amb moltíssims usuaris simultanis, això consumeix més recursos del servidor que Blazor WebAssembly, on cada navegador executa la seva pròpia còpia del codi —una consideració d'escalabilitat a tenir en compte en triar el model.
Exercicis
-
Afegeix a
Cataleg.razorun<input @bind="textCerca" />i filtra la taula mostrada perquè només hi apareguin els materials elTitoldels quals contingui el text introduït (fes servirContains, ja vist al Mòdul 1, i actualitza el filtre a cada pulsació mitjançant@bind:event="oninput"). -
Afegeix un comptador
<p>Total disponibles: @Biblioteca.Cataleg.Count(m => m.Disponible)</p>a sobre de la taula, i explica en un comentari per què no cal cap codi addicional perquè s'actualitzi després de cada préstec.
Solucions
<input @bind="textCerca" @bind:event="oninput" placeholder="Cercar per titol..." />
<table>
<tbody>
@foreach (MaterialBibliotecari material in Biblioteca.Cataleg.Where(
m => m.Titol.Contains(textCerca, StringComparison.OrdinalIgnoreCase)))
{
<tr>
<td>@material.Titol</td>
<td>@material.Autor</td>
</tr>
}
</tbody>
</table>
@code {
private string textCerca = string.Empty;
// ... resta del codi existent ...
}
<p>Total disponibles: @Biblioteca.Cataleg.Count(m => m.Disponible)</p>
@* No cal codi addicional perque Blazor torna a renderitzar el component sencer
despres de cada esdeveniment gestionat (com el @onclick de PrestarMaterial); en renderitzar
de nou, aquesta expressio es reavalua automaticament contra l'estat actual de
Biblioteca.Cataleg, sense necessitat d'actualitzar manualment cap variable. *@
Conclusió
En aquesta lliçó has vist com Blazor porta C# al navegador, amb dos models d'execució —Server i
WebAssembly— que canvien completament on viu la lògica i com s'accedeix a les dades, components
.razor que combinen marcatge i codi, @bind/@onclick per a la interacció, i @inject per
obtenir serveis del contenidor de dependències. El component Cataleg.razor reutilitza, una
vegada més, el mateix Biblioteca.Cataleg i el mateix material.Prestar() vistos des del Mòdul
2, ara accessibles des de qualsevol navegador.
L'última lliçó d'aquest mòdul presenta Xamarin i .NET MAUI, que porten BiblioTech a dispositius mòbils i d'escriptori amb un únic projecte multiplataforma, fent servir un XAML molt semblant al de WPF —tancant així el recorregut per les principals formes de construir interfícies en C#, abans que el Mòdul 8 poleixi el codi de tot el que s'ha construït amb bones pràctiques i patrons de disseny.
Curs de Programació en C#
Mòdul 1: Introducció al C#
- Introducció al C#
- Configuració de l'Entorn de Desenvolupament
- Programa Hola Món
- Sintaxi i Estructura Bàsica
- Variables i Tipus de Dades
- Arrays i Cadenes de Text
Mòdul 2: Estructures de Control
Mòdul 3: Programació Orientada a Objectes
- Classes i Objectes
- Mètodes
- Constructors i Destructors
- Herència
- Polimorfisme
- Encapsulació
- Abstracció
- Structs i Records: Tipus per Valor i per Referència
Mòdul 4: Conceptes Avançats de C#
- Interfícies
- Delegats i Esdeveniments
- Pattern Matching i Característiques Modernes de C#
- Genèrics
- Col·leccions
- LINQ (Consulta Integrada en el Llenguatge)
- Programació Asíncrona
Mòdul 5: Treballant amb Dades
- Entrada/Sortida de Fitxers
- Serialització
- Connectivitat amb Bases de Dades
- Entity Framework
- Treball amb JSON i Consum d'APIs REST
Mòdul 6: Temes Avançats
- Reflexió
- Atributs
- Programació Dinàmica
- Gestió de Memòria i Recollida d'Escombraries
- Multifil i Programació Paral·lela
Mòdul 7: Construcció d'Aplicacions
Mòdul 8: Bones Pràctiques i Patrons de Disseny
- Estàndards de Codificació i Bones Pràctiques
- Patrons de Disseny
- Injecció de Dependències i Inversió de Control
- Proves Unitàries
- Revisió i Refactorització de Codi
