Nell’articolo precedente abbiamo visto cosa è Blazor e quali sono le dinamiche basilari che regolano questo framework. Oggi iniziamo a vedere come è strutturato un componente Razor, alla base di questo framework.
Ricordiamo che Razor è il modo in cui andiamo a scrivere i nostri componenti, in un’unica pagina possiamo quindi trovare Html, Css, C# (ma anche Javascript).
Componenti
Quando costruiamo una UI in Blazor dobbiamo cercare di creare tanti piccoli componenti con l’obiettivo di riutilizzarli. Nel progetto i componenti hanno l’estensione .razor:

Ma come facciamo a richiamare un componente? La risposta è molto semplice. Prendiamo il componente Counter.razor, per richiamarlo sarà sufficiente utilizzare un tag con il nome del file, in questo caso <counter></counter>
.
@page "/count-element"
<PageTitle>Element counter page</PageTitle>
<h1>Counter</h1>
<Counter></Counter>
Struttura delle pagine
Passiamo ora a vedere come è strutturata una pagina di un componente. Quella rappresentata sotto è un semplice esempio composto da un titolo, una textbox ed un pulsante per cambiarlo. Vediamo i vari elementi nel dettaglio
@page "/"
<PageTitle> @title </PageTitle>
<h1>Page title: @title </h1>
<p>Welcome to your new app.</p>
<br/>
<input type="text" @bind="title" />
<button @onclick="ResetTitle">Reset title</button>
@code {
private string title = "Hello World";
private void ResetTitle()
{
title = "Hello World"
}
}
@page “/”
Con questa direttiva andiamo a definire la route di questa pagina. una volta avviata l’applicazione questa sarà la pagina di default, per esempio http://thinkasadev.com/.
Se avessimo la direttiva @page "/page-2"
la route finale sarebbe stata http://thinkasadev.com/page-2.
<PageTitle> @title </PageTitle>
Questo tag ci permette di andare a configurare il tutolo della nostra pagina. Da notare che il titolo è il contenuto della variabile @title
Variabile @title
Blazor supporta il 2-Way-Data-Binding, questo significa che le modifiche effettuate dall’utente su un determinato campi si rifletteranno sul modello, viceversa se un modello cambia verrà aggiornato anche il valore sulla UI.
La variabile @title
è dichiarata nella sezione Code
in fondo alla pagina
<input type=”text” @bind=”title” />
Qui incontriamo la prima direttiva di questo componente, la direttiva @bind
. Blazor supporta il 2-way-data-binding, ogni modifica fatta nella textbox aggiornerà il valore di title
, inoltre tutte le modifiche fatte al valore di title
aggiorneranno ciò che contiene la textbox. Nell’immagine a fine di questo elenco è possibile vedere questo aspetto.
Nel caso in cui avessimo una variabile di tipo Booleano, potremmo utilizzare @bind
anche in una checkbox, in automatico sarà gestito l’attributo checked
del componente.
<button @onclick=”ResetTitle”>Change title</button>
Qui abbiamo invece l’esempio di un evento: @onclick
scatenerà il richiamo della funzione ResetTitle()
, definito nella porzione di codice in fondo al nostro esempio. Da notare che gli elementi seguiti da @ sono interpretati come sintassi Razor e possono essere inseriti all’interno della pagina o all’interno di tag html.
@code { …. }
Questa è la sezione della pagina dove andiamo ad inserire il codice, funzioni e variabili.
ATTENZIONE
Uno degli errori più comuni è quello di andare a scrivere tonnellate di codice all’interno di questa sezione, rendendo il componente pesante e poco leggibile. Per ovviare a questo ci sono diverse strategie, tra le quali spicca la possibilità di usare la Dependency Injection direttamente nel componente. Se un componente cresce è bene “spezzarlo” in tante parti, realizzando componenti più piccoli e più semplici da gestire.
L’effetto finale sarà il seguente:

Eventi
Nell’esempio di prima abbiamo visto l’evento per gestire il click di un pulsante, se non ci troviamo nella modalità WebAssembly verrà inviato l’evento al server il quale aggiornerà il DOM e lo rispedirà al client. Il tutto genererà un singolo evento.

In questo secondo esempio abbiamo implementato l’evento @onchange
per gestire il cambiamento di testo all’interno di una textbox. Da notare che il DOM sarà aggiornato nel momento in cui interagiremo con la pagina, cliccando un componente o anche cliccando sulla parte bianca della pagina.
@page "/"
@rendermode InteractiveServer
<label>Name</label>
<input type="text" @onchange="OnNameChanged" />
<label>Result: @firstName</label>
@code {
private string firstName = "";
void OnNameChanged(ChangeEventArgs e)
{
firstName = (string)e.Value;
}
}
Alcuni eventi, come ad esempio l’evento @onkeyup
, si genera molto più frequentemente rispetto all’evento @onchange: in questo caso ad ogni tasto premuto verrà generato un evento che signalR invierà al server, che elaborerà la modifica e ritornerà il risultato finale.
@page "/onchange"
@rendermode InteractiveServer
<label>Name:</label>
<input type="text" @onkeyup="OnNameChanged" />
<label>Result: @firstName</label>
@code {
private string firstName = "";
void OnNameChanged(KeyboardEventArgs e)
{
//If backspace remove last item
if (e.Key == "Backspace")
{
if (firstName.Length > 0)
{
firstName = firstName.Substring(0, firstName.Length - 1);
}
}
else
{
//handle Shift, space, symbols,..
firstName += e.Key;
}
}
}
ATTENZIONE
Questo comportamento è da tenere in considerazione nel momento in cui andiamo a sviluppare un’applicazione, nel secondo scenario se scriviamo “Giorgio” genereremo 7 eventi, uno per ogni lettera. Nel primo scenario ( @onclick
), l’evento generato è solamente 1. Ne consegue che il carico gestito dal server sarà sicuramente maggiore se usiamo @onkeyup
rispetto a @onclick
.
Nel caso in cui ci siano troppi eventi e la connessione non è ottimale (o il server non riuscisse a gestire tutto il carico) l’esperienza utente sarà peggiore in quanto potrebbe verificarsi dei lag e dei rallentamenti.

Funzioni eventi con parametri
A differenza delle funzioni javascript, dove specifichiamo sia la funzione che eventuali argomenti (es. onclick="myFunction('argument1', 'argument2'
)” in Blazor andiamo a richiamare la funzione solamente tramite il suo nome (es. @onclick="MyFunction"
)
Come facciamo se volessimo passargli degli argomenti? Possiamo sfruttare le espressioni lambda, direttamente sull’evento del nostro componente:
@page "/my-action"
<h1>Actoin</h1>
<p>Action done: @myAction</p>
<button class="btn btn-primary" @onclick='() => DoMyAction("Button clicked")'>Click me</button>
@code {
private string myAction = "No action done";
private void DoMyAction(string action){
myAction = $"Action: {action}";
}
}
Alcuni eventi richiedono che la funzione abbia un parametro per passare il contesto, pensiamo ad esempio all’evento @onkeyup usato negli esempi precedenti.
<input type="text" @onkeyup="OnNameChanged" />
@code{
void OnNameChanged(KeyboardEventArgs e)
{
//code
}
}
Come facciamo se volessimo passare un secondo argomento a questa funzione? La risposta è sempre utilizzare le lamba expression, passando l’argomento come parametro, ecco un esempio:
<input type="text" @onkeyup='(KeyboardEventArgs e) => OnNameChanged(e, "Hello!")' />
@code{
void OnNameChanged(KeyboardEventArgs e, string myMessage)
{
//code
}
}
Override delle funzioni degli eventi
Tutti gli eventi blazor hanno una funzione di default che scatta, a prescindere dal fatto che noi andiamo ad aggiungere una nostra funzione.
Prendiamo come esempio l’evento @onkeypress specificato qui sotto:
<input value=@itemValue/>
<br/>
<input value=@itemValue2 @onkeypress="MyFunction"/>
@code {
private string itemValue;
private string itemValue2;
private async Task MyFunction(KeyboardEventArgs e)
{
//logic
}
}
Le due funzioni descritte sopra sono uguali, tranne per il fatto che la seconda richiamerà anche la nostra funzione.
TODO immagine?
Ipotizziamo di voler implementare un controllo che permetta l’inserimento di soli numeri, tutti gli altri caratteri devono essere bloccati. A prescindere da quello che andremo ad implementare il carattere digitato da tastiera sarà inserito nella nostra textbox: sarà la funzione di default che si occuperà di fare ciò
aasdasd
Ulteriori dettagli sugli eventi li trovate sulla pagina ufficiale di Microsoft, dove trovate anche la lista completa degli eventi gestiti con le loro caratteristiche.
Bind
In Blazor possiamo eseguire il bind di un valore tramite la direttiva razor @bind
, ecco alcuni esempi:
@page "/binding"
@rendermode InteractiveServer
<PageTitle>Binding example</PageTitle>
<input type="text" @bind="textValue" />
<label>Value: @textValue</label>
<hr/>
<input type="checkbox" @bind="booleanValue" />
<label>Value: @booleanValue</label>
<hr />
<select @bind="optionValue">
<option value="opt1">Option 1</option>
<option value="opt2">Option 2</option>
<option value="opt3">Option 3</option>
</select>
<label>Value: @optionValue</label>
<hr />
<input type="number" step="1" @bind="numberValue" />
<label>Value: @numberValue</label>
@code {
private string textValue = "";
private string optionValue = "opt2";
private bool booleanValue = true;
private int numberValue = 1;
}

Scegliere l’evento di binding
Il binding della prima textbox scatena l’evento solamente se premiamo il tasto Enter o se usciamo dal contesto della textbox (es. cliccando altrove nella pagina o premendo tab). Per aggiornare il valore in tempo reale, ad ogni tasto premuto allora dobbiamo impostare così il nostro codice:
<input @bind="textValue" @bind:event="oninput" />
<label>Value: @textValue</label>
la direttiva @bind:event
ci permette di specificare su quale situazione scatenare l’evento di aggiornamento, in questo caso verrà scatenata ad ogni lettera inserita.

Richiamare una funzione dopo l’evento di bing
Come facciamo a richiamare una funzione (sincrona o asincrona) a seguito della modifica del testo di una textbox? Semplice, con @bind:after @bind:afterAsync
! Ecco un esempio:
<!--Call sync function-->
<input @bind="searchText" @bind:event="oninput" @bind:after="findElements" />
<!--Call async function-->
<input @bind="searchText" @bind:event="oninput" @bind:afterAsync="findElementsAsync" />
@code {
private string? searchText;
private string[]? searchResult;
private async Task findElementsAsync() {
//Application logic
}
private void findElements() {
//Application logic
}
}
Alla riga 2 e alla riga 5 potete vedere come, a seguito dell’inserimento di un carattere nella textbox, verranno richiamate rispettivamente le funzioni findElements
e findElementsAsync
.
Come impostare il 1-way-data-binding
Blazor supporta il 2-way-data-binding di default, questo significa che tramite la direttiva @bind
ogni elemento “trasmette” e riceve” modifiche del suo oggetto.
Non sempre però questa situazione è quella che vogliamo ottenere, potremmo trovarci nella condizione di voler solamente un binding ad una via, anche per non appesantire il server con gestioni superflue, in altri casi invece potremmo avere la necessità di richiamare una funzione specifica.
In un progetto ho avuto la necessità di validare l’input inserito in una textbox e “calcolare” il valore rispetto ad altri parametri nella pagina. Alla fine ho utilizzato questa strategia:
<label>Codice</label>
<input type="text" @bind:get="code" @bind:set="CalculateCodeValue" />
@code {
private string code= "Example_code_value";
private Task CalculateCodeValueAsync(string value)
{
//application logic to elaborate value (user input) and set code
code = value.Replace(" ", "_"); // --> Example
return Task.CompletedTask;
}
}
In questo caso veniva proposto un valore di default (“Example code value“) ma il suo valore finale era ri-elaborato dopo che l’utente aveva inserito il suo nuovo codice. Dopo l’evento di binding si attiverà la funzione CalculateCodeValueAsync
(dove troviamo il testo inserito dall’utente nel parametro value
).
Per ulteriori approfondimenti rispetto a questo tema vi consiglio di dare leggere la documentazione di Microsoft, disponibile a questo indirizzo.
Conclusioni
In questo articolo abbiamo visto alcuni degli elementi base che utilizziamo per creare una pagina.
Come già abbiamo detto il framework ha differenti modalità per gestire il DOM, nella modalità Blazor Server la pagina viene aggiornata lato server in funzione degli eventi che accadono sulla UI e rimandata al client: dobbiamo tenerne conto quando andiamo a sviluppare un componente, poiché una cattiva gestione degli eventi potrebbe portare ad un inutile sovraccarico del server.
Abbiamo poi proseguito sul tema del binding e visto come è possibile gestire l’aggiornamento dei dati del modello sui vari elementi della UI.
Grazie a tutti per la lettura, ci vediamo al prossimo articolo dedicato a Blazor!