In Blazor, per presentare a video i dati presenti in un modello e salvare ciò che l'utente inserisce all'interno di un input, viene utilizzata una logica definita Two-way data binding. Questi processi permettono di mantenere una sincronia in tempo reale tra modello logico e dati rappresentati: come mostrato nell'esempio che troviamo all'interno di ogni nuova applicazione, se premiamo il bottone, nella pagina Counter, il numero mostrato viene immediatamente incrementato.
Perchè se è una funzionalità così performante, abbiamo bisogno di inserire un debounce, ossia qualcosa che dilazioni il numero di aggiornamenti? Vi sono dei contesti, anche molto comuni, in cui l'elevato numero di aggiornamenti genera del rumore non voluto, rendendo i processi della pagina lenti e volubili ad errori.
Per capire meglio utilizziamo la seconda pagina di esempio, la Weather forecast, e immaginiamo di voler aggiungere un input in cima alla tabella per filtrare i dati. La logica è molto semplice: prevede l'utilizzo di una proprietà di tipo string, che al cambiamento aggiorni una lista che sarà composta da tutti gli elementi della lista originale che hanno superato la scrematura.
<input class="form-control" @bind-value="SearchText" @bind-value:event="oninput"/> <Virtualize Items="@FilteredList" Context="weather"> @weather.Location </Virtualiza> @code{ public string SearchText = ""; IList<WeatherForecast> FilteredList { get => originalList.Where( weather => weather.Location.Contains(SearchText)).ToList(); } IList<WeatherForecast> originalList = new List<WeatherForecast>(); }
Per motivi dimostrativi abbiamo ridotto il codice mostrato, ma nel caso reale ci sarebbe stata una tabella con molte righe e colonne, che vengono create e distrutte ad ogni input dell'utente. Se inserissimo una parola come "MILANO", la logica di aggiornamento ricreerà la tabella 6 volte. Sarebbe dunque più funzionale se, prima di lanciare l'evento che aggiorni la proprietà SearchText, si attendesse del tempo, in modo da lasciare all'utente la possibilità di inserire più lettere.
Per arrivare a ciò non abbiamo proprietà o nulla di automatico, ma possiamo crearci i nostri metodi, ricordandoci che tutto quello che lega ciò che scriviamo all'interno del browser, al codice C#, sono eventi javascript, che possono essere intercettati e gestiti.
Partiamo proprio dalla scrittura di una funzione che ci permetterà di rimandare di un certo numero di secondi l'emissione dell'evento e che allo stesso tempo permetta di annullare l'evento precedente se viene richiamata una seconda volta entro il tempo prefissato. Aggiungiamo un file nella seguente posizione wwwroot/js/events.js
// funzione principale che verrà chiamata da Blazor fornendo // l'elemento html // la tipologia di evento da gestire // l'intervallo di tempo export function debounceEvent(htmlElement, eventName, delay) { registerEvent(htmlElement, eventName, delay, debounce); } function registerEvent(htmlElement, eventName, delay, filterFunction) { let raisingEvent = false; // creazione dell'evento let eventHandler = filterFunction(function (e) { raisingEvent = true; try { // se si supera il tempo pre-impostato, viene eseguito il dispatch dell'evento htmlElement.dispatchEvent(e); } finally { raisingEvent = false; } }, delay); // registrazione dell'evento sull'elemento htmlElement.addEventListener(eventName, e => { if (!raisingEvent) { e.stopImmediatePropagation(); eventHandler(e); } }); } // funzione che permette di incapsulare la funzione all'interno di un timer function debounce(func, wait) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, wait); }; }
Quello che abbiamo creato pocanzi è un modulo javascript, che andrà opportunamente importato alla bisogna, utilizzando l'istanza dell'interfaccia IJSRuntime, come mostrato in seguito
public static class EventExtensions { public static async Task DebounceEvent(this IJSRuntime jsRuntime, ElementReference element, string eventName, TimeSpan delay) { await using var module = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/events.js"); await module.InvokeVoidAsync("debounceEvent", element, eventName, (long)delay.TotalMilliseconds); } }
Uniamo i pezzi del puzzle nella pagina Weather forecast, all'interno della quale occorrerà ottenere una referenza all'input HTML di ricerca e richiamare l'extension method per impostare la logica con cui controllare gli eventi di input
@inject IJSRuntime JSRuntime <input @ref="searchInput" class="form-control" @bind-value="SearchText" @bind-value:event="oninput"/> <Virtualize Items="@FilteredList" Context="weather"> @weather.Location </Virtualiza> @code{ ElementReference searchInput; public string SearchText = ""; IList<WeatherForecast> FilteredList { get => originalList.Where( weather => weather.Location.Contains(SearchText)).ToList(); } IList<WeatherForecast> originalList = new List<WeatherForecast>(); protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await JSRuntime.DebounceEvent(searchInput, "input", TimeSpan.FromMilliseconds(500)); } } }
Grazie alla struttura modulare e all'extension method basato sull'interfaccia JSRuntime, possiamo applicare il debounce a qualsiasi tipologia di evento di un qualsiasi elemento, senza appesantire il codice o l'esecuzione.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Evitare la script injection nelle GitHub Actions
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
Triggerare una pipeline su un altro repository di Azure DevOps
Cancellare una run di un workflow di GitHub
Generare la software bill of material (SBOM) in GitHub
Assegnare un valore di default a un parametro di una lambda in C#
Filtrare i dati di una QuickGrid in Blazor con una drop down list
Eseguire query manipolando le liste contenute in un oggetto mappato verso una colonna JSON
Modificare i metadati nell'head dell'HTML di una Blazor Web App
Sfruttare lo stream rendering per le pagine statiche di Blazor 8
Aggiungere interattività lato server in Blazor 8
Eseguire i worklow di GitHub su runner potenziati
I più letti di oggi
- anche domani dalle 17:30 seguite con noi live #build15 https://aspit.co/build15
- Webcast 'AJAX & ATLAS Overview'
- a #igds il 25 e 26/10 a Milano puoi sviluppare un gioco per #wp8 con @AppCampus. in palio 70.000 Euro: https://aspit.co/apa
- WinJS in Windows Phone 8.1
- la RC di #vs13 è compatibile con #win81 RTM, non con la Preview. l'annuncio ufficiale è su https://aspit.co/any
- Ancora un bug: esce Mono 0.23
- SSL Certificates for everyone on Azure
- Mostrare una MessageBox con un custom control
- disponibile la preview 1 ci #dotnetcore 2.1, #aspnetcore, #efcore. performance, novità e migliorie su https://aspit.co/bmf
- si continua a #netconfit con 'developing modern web apps with #aspnetcore', con il nostro @dbochicchiohttps://aspit.co/netconf-18