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

  1. Què és Blazor: C# també al navegador
  2. Els dos models: Blazor Server davant Blazor WebAssembly
  3. Components .razor: estructura bàsica
  4. Sintaxi de Razor: @code, @bind, @onclick
  5. Cicle de vida d'un component: OnInitializedAsync
  6. Injecció de dependències en un component: @inject
  7. Exemple complet: Cataleg.razor per a BiblioTech
  8. Quan triar Blazor davant ASP.NET Core "pur" o davant WPF

  1. 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.

  1. 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.

  1. Components .razor: estructura bàsica

Un 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.

  1. Sintaxi de Razor: @code, @bind, @onclick

Dins del marcatge, qualsevol expressió C# s'introdueix amb @:

<p>Total de llibres: @totalLlibres</p>

@code {
    private int totalLlibres = 2;
}

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).

  1. Cicle de vida d'un component: OnInitializedAsync

Un 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.

  1. Injecció de dependències en un component: @inject

Un 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

<p>Total de materials: @Biblioteca.Cataleg.Count</p>

@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.

  1. Exemple complet: Cataleg.razor per a BiblioTech

Unint 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.

  1. 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 Biblioteca directament (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 —caldria HttpClient contra 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ètode PrestarMaterial ho detecta igualment (per la comprovació de Disponible), 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

  1. Afegeix a Cataleg.razor un <input @bind="textCerca" /> i filtra la taula mostrada perquè només hi apareguin els materials el Titol dels quals contingui el text introduït (fes servir Contains, ja vist al Mòdul 1, i actualitza el filtre a cada pulsació mitjançant @bind:event="oninput").

  2. 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#

Mòdul 2: Estructures de Control

Mòdul 3: Programació Orientada a Objectes

Mòdul 4: Conceptes Avançats de C#

Mòdul 5: Treballant amb Dades

Mòdul 6: Temes Avançats

Mòdul 7: Construcció d'Aplicacions

Mòdul 8: Bones Pràctiques i Patrons de Disseny

Mòdul 9: Projecte Final

© Copyright 2026. Tots els drets reservats