Negli script precedenti abbiamo iniziato un breve viaggio nel sopperire a una delle mancanze di Blazor, costruendo un generatore dinamico di URL in base al routing, analogamente a come avviene in ASP.NET MVC. Abbiamo inizialmente impostato la nostra soluzione, con alcune limitazioni (https://www.aspitalia.com/script/1390/Creare-Link-Fortemente-Tipizzati-Blazor.aspx) e poi abbiamo visto come supportare eventuali parametri (https://www.aspitalia.com/script/1391/Generare-URL-Strongly-Typed-Parametri-Blazor.aspx). Con questo script chiuderemo il cerchio, e vedremo come gestire componenti che potenzialmente hanno più di una route definita.
Come sappiamo infatti, dato che Blazor non supporta parametri opzionali o valori di default nel routing, l'unico modo per implementare questi concetti è quello di associare più di una route allo stesso componente:
@page "/counter" @page "/counter/{start:int}" @page "/counter/{start:int}/{end:int}" <h1>Counter</h1> <p>Current count: @Start</p> ...
Prima di aggiungere questa feature al nostro codice, conviene però effettuare un piccolo refactoring della nostra soluzione precedente, creando una classe ParsedRoute che rappresenta una route della quale abbiamo recuperato il template di base e i parametri:
public class ParsedRoute { public string Template { get; private set; } public string[] Parameters { get; private set; } public ParsedRoute(string template) { this.Template = template; this.Parameters = Regex.Matches(template, "{(\\w+)(:.+?)?}") .Select(x => x.Groups[1].Value) .ToArray(); } public string Render(RouteValueDictionary values) { string url = this.Template; foreach (var key in values.Keys) { url = Regex.Replace(url, $"{{{key}(:.+?)?}}", values[key].ToString(), RegexOptions.IgnoreCase); } return url; } }
Questa classe può essere costruita a partire da un template, e usa le regular expression per popolare un array con tutti i parametri presenti nella route stessa.
Allo stesso tempo, nel metodo Render incapsula la logica per rimpiazzare i parametri con i valori provenienti da un RouteValueDictionary, tramite la stessa tecnica che abbiamo visto nello script precedente, restituendo la stringa che rappresenta l'URL finale.
Anche la nostra classe RouteHelper a questo punto necessita di alcune modifiche, per incorporare il nuovo oggetto che abbiamo appena creato. In particolare, contiene un metodo privato ParseRoutes in grado di restituire una collection di ParsedRoute a partire da quelle definite nel componente stesso:
public static class RouteHelper { private static IEnumerable<ParsedRoute> ParseRoutes<TComponent>() where TComponent : ComponentBase { Type type = typeof(TComponent); var result = type.GetCustomAttributes(typeof(RouteAttribute), inherit: false) .OfType<RouteAttribute>() .Select(x => new ParsedRoute(x.Template)); return result; } ... }
Il codice è facilmente comprensibile, grazie all'utilizzo di LINQ: non facciamo altro che individuare tutti gli attributi RouteAttribute associati al componente, e con ognuno di questi, generiamo il corrispondente ParsedRoute sfruttandone il template.
Ora non ci resta che utilizzare questo metodo per generare l'URL richiesto, a partire dai parametri forniti in ingresso:
public static string GetUrl<TComponent>(object parameters = null) where TComponent : ComponentBase { var properties = new RouteValueDictionary(parameters); var result = ParseRoutes<TComponent>() // stesso numero di parametri in input .Where(x => x.Parameters.Length == properties.Count) // tutti i nomi dei parametri corrispondono all'input .Where(x => x.Parameters.All(parameter => properties.ContainsKey(parameter))) .Select(x => x.Render(properties)) .FirstOrDefault(); return result; }
Anche questo metodo sfrutta una query LINQ che, almeno all'apparenza, può sembrare leggermente più complessa della precedente. In buona sostanza, ciò che facciamo è, innanzitutto, popolare un RouteValueDictionary con le proprietà dell'oggetto parameters in input, e successivamente cerchiamo, tra tutte le ParsedRoute disponibili, quella che abbia lo stesso numero e nomi di parametri.
Nel caso questa route esista, ne invochiamo il metodo Render visto prima per sostituire i parametri e generare il risultato richiesto. In caso contrario, il metodo comunque non fallisce, ma l'URL restituito avrà valore null.
Grazie a questo nuovo oggetto, ora possiamo selezionare la route desiderata per il componente Counter che abbiamo visto all'inizio dello script, semplicemente specificando i parametri corrispondenti:
<NavLink class="nav-link" href="@(RouteHelper.GetUrl<Counter>(new { Start = 4 }))"> <span class="oi oi-plus" aria-hidden="true"></span> Counter with Start </NavLink> <NavLink class="nav-link" href="@(RouteHelper.GetUrl<Counter>(new { Start = 4, End = 10 }))"> <span class="oi oi-plus" aria-hidden="true"></span> Counter with Start/End </NavLink>
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
.NET Conference Italia 2023
Usare una container image come runner di GitHub Actions
Utilizzare l'operatore GroupBy come ultima istruzione di una query LINQ in Entity Framework
Ottimizzare le performance delle collection con le classi FrozenSet e FrozenDictionary
Eseguire una GroupBy per entity in Entity Framework
Aggiungere interattività lato server in Blazor 8
Utilizzare i primary constructor in C#
Sfruttare lo stream rendering per le pagine statiche di Blazor 8
Effettuare lo stream della risposta in ASP.NET Core tramite IAsyncEnumerable
Visualizzare le change sul plan di Terraform tramite le GitHub Actions
Generare token per autenicarsi sulle API di GitHub
Eseguire attività basate su eventi con Azure Container Jobs
I più letti di oggi
- Vuoi incontrare Bill Gates? Viaggia con ASPItalia.com!
- Customizzare il pager del DataGrid
- Stabilire un collegamento VPN tra una Web App e una Virtual Network
- Documentare ASP.NET Web API con Swagger
- Usare i servizi REST di BING per ottenere informazioni sulla posizione dell'utente
- Visual Studio 2005 CTP May