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
Supporto ai tipi DateOnly e TimeOnly in Entity Framework Core
Creare un'applicazione React e configurare Tailwind CSS
Ordine e importanza per @layer in CSS
Gestire domini wildcard in Azure Container Apps
Sfruttare gli embedding e la ricerca vettoriale con Azure SQL Database
Utilizzare Azure AI Studio per testare i modelli AI
Configurare il nome della run di un workflow di GitHub in base al contesto di esecuzione
Creare gruppi di client per Event Grid MQTT
Usare i servizi di Azure OpenAI e ChatGPT in ASP.NET Core con Semantic Kernel
Sfruttare MQTT in cloud e in edge con Azure Event Grid
Rinnovare il token di una GitHub App durante l'esecuzione di un workflow
Registrare servizi multipli tramite chiavi in ASP.NET Core 8