In uno script precedente abbiamo visto come il model binder di default di ASP.NET MVC presenti già un supporto out-of-the-box alle regole di globalizzazione. Purtroppo questo non è altrettanto vero per la validazione client-side: essa, infatti, avviene sempre secondo la culture americana, a prescindere da quella dell'utente. Per far sì che il nostro sito web funzioni correttamente con tutte le culture, è necessario personalizzare la logica utilizzata da jQuery Validator per effettuare il parsing di numeri decimali.
Il modo più semplice è quello di affidarsi a jQuery Globalize, un plugin di jQuery che espone funzioni per il parsing e la formattazione di dati nelle diverse culture; l'installazione è assolutamente banale, visto che esiste un package di NuGet che contiene, in buona sostanza:
- Uno script globalize.js, che espone i metodi e le funzioni di questa libreria;
- Un gran numero di script globalize.culture.xx-XX.js, ognuno dei quali contiene le regole proprie di ogni culture.
Il primo passo, allora è quello di aggiungere le necessarie reference alla nostra layout page, in base alle informazioni della culture utente:
<script src="@Url.Content("~/Scripts/jquery.globalize/globalize.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.globalize/cultures/globalize.culture." + System.Threading.Thread.CurrentThread.CurrentUICulture.ToString() + ".js")" type="text/javascript"></script>
Come possiamo notare, in particolare, il secondo link è calcolato a runtime, in base al valore della proprietà CurrentUICulture del thread corrente.
Il passo successivo, è quello di selezionare la culture da utilizzare e modificare la funzione di parsing dei number utilizzata da jQuery Validator:
<script type="text/javascript"> $(function () { Globalize.culture( '@System.Threading.Thread.CurrentThread.CurrentUICulture.ToString()'); }); $.validator.methods.number = function (value, element) { if (value.indexOf(Globalize.culture().numberFormat[',']) > -1) { return false; } if (!isNaN(Globalize.parseFloat(value))) { return true; } return false; } </script>
In pratica, dopo aver selezionato la culture desiderata, non facciamo altro che verificare che il numero digitato dall'utente possa essere correttamente interpretato dalla funzione Globalize.parseFloat(). Il primo controllo, invece, serve a considerare come invalidi gli input che contengono il separatore delle migliaia: in questo modo siamo sicuri che il numero "1.000" nella culture italiana non sia interpretato come "1000".
Con questi semplici passaggi, siamo effettivamente in grado di estendere le nostre regole di globalizzazione anche alla validazione lato client. L'unico aspetto migliorabile è che, come nel caso del model binder visto nel precedente script, l'eventuale messaggio di errore è scritto in lingua inglese. Purtroppo questa volta la soluzione non è altrettanto semplice, dato che questo messaggio è prodotto dalla classe ClientDataTypeModelValidatorProvider che non prevede alcun supporto a file di risorse esterne.
L'unica alternativa, allora, è quella di realizzarne una versione personalizzata come nel codice seguente:
public class GlobalClientDataTypeModelValidatorProvider : ClientDataTypeModelValidatorProvider { public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) { var result = base.GetValidators(metadata, context); if (result.Count() > 0) yield return new GlobalNumericModelValidator(metadata, context); } }
L'oggetto GlobalNumericModelValidator restituito sostituisce il NumericModelValidator standard del framework, che ha il compito di generare il messaggio di errore della validazione client side di un numero.
GlobalNumericModelValidator è assolutamente identico a quello di default, tranne il fatto che utilizza il file di risorse del model binder per determinare il messaggio di errore corretto:
public class GlobalNumericModelValidator : ModelValidator { public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() { ModelClientValidationRule rule = new ModelClientValidationRule { ValidationType = "number", ErrorMessage = MakeErrorString(base.Metadata.GetDisplayName()) }; return new ModelClientValidationRule[] { rule }; } private string MakeErrorString(string displayName) { string format = this.ControllerContext.HttpContext.GetGlobalResourceObject( DefaultModelBinder.ResourceClassKey, "Client_PropertyValueInvalid", CultureInfo.CurrentUICulture) as string; return string.Format(CultureInfo.CurrentCulture, format, new object[] { displayName }); } }
L'ultimo passo da compiere consiste nel configurare il runtime affinché sfrutti questo nostro set di classi in luogo di quelle standard. Ancora una volta, il posto più opportuno per farlo è il metodo Application_Start di global.asax:
protected void Application_Start() { // altro codice qui... var provider = ModelValidatorProviders.Providers .OfType<ClientDataTypeModelValidatorProvider>() .Single(); ModelValidatorProviders.Providers.Remove(provider); ModelValidatorProviders.Providers.Add( new GlobalClientDataTypeModelValidatorProvider()); }
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Personalizzare l'errore del rate limiting middleware in ASP.NET Core
Effettuare il deploy di immagini solo da container registry approvati in Kubernetes
Evitare (o ridurre) il repo-jacking sulle GitHub Actions
Eseguire query manipolando le liste contenute in un oggetto mappato verso una colonna JSON
Usare le variabili per personalizzare gli stili CSS
Sfruttare lo streaming di una chiamata Http da Blazor
Load test di ASP.NET Core con k6
Utilizzare la libreria Benchmark.NET per misurare le performance
Utilizzare le Cache API di JavaScript per salvare elementi nella cache del browser
Applicare il versioning ai nostri endpoint ASP.NET Core Minimal API
Accesso sicuro ai secrets attraverso i file in Azure Container Apps
Utilizzare HiLo per ottimizzare le insert in un database con Entity Framework
I più letti di oggi
- Registrare in una matrice il contenuto di una tabella di un database
- Monad diventa Windows PowerShell e va in RC1
- Nasce il DLR: il .NET Framework 3.5 supporta i linguaggi dinamici
- Using Components in Blazor
- Un Rating Custom Control con DropDownList e jQuery
- Performance in .NET 6 https://aspit.co/b9g di @laxxifer