I\'m trying to achieve client-side validation for a nullable
whose decimal separator can be a comma (e.g.: 123,45).
In my View:
I've struggled quite a bit with this.
The best approach for me is to define a custom binder for decimals:
public class DecimalModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
ValueProviderResult valueResult = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName);
ModelState modelState = new ModelState { Value = valueResult };
object actualValue = null;
try
{
//Check if this is a nullable decimal and a null or empty string has been passed
var isNullableAndNull = (bindingContext.ModelMetadata.IsNullableValueType &&
string.IsNullOrEmpty(valueResult.AttemptedValue));
//If not nullable and null then we should try and parse the decimal
if (!isNullableAndNull)
{
actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
}
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
and bind it in the Application_Start
in Global.asax
:
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
I also use the globalize script (with cultures) which can be found here or downloaded from nuget here.
Your bundle should look something like this:
bundles.Add(ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js",
"~/Scripts/globalize.js",
"~/Scripts/cultures/globalize.culture.en-GB.js",
"~/Scripts/cultures/globalize.culture.it-IT.js"
));
Of course you can add more culures if you want to support different localizations.
Now, when your DOM is ready (javascript) you can define your culture:
Globalize.culture('en-GB');
$.validator.methods.number = function (value, element) {
return this.optional(element) || jQuery.isNumeric(Globalize.parseFloat(value));
}
//Fix the range to use globalized methods
jQuery.extend(jQuery.validator.methods, {
range: function (value, element, param) {
var val = Globalize.parseFloat(value);
return this.optional(element) || (val >= param[0] && val <= param[1]);
}
});
$.validator.methods.date = function (value, element) {
return (this.optional(element) || Globalize.parseDate(value));
}
and customize your validations (I've added the date as well). You've done that in your jQueryFixes
.
You can find a working example here (MvcAppForDecimals) where you can change languages using a toolbar and cookies so that the culture can change on the server as well.
In the example I read the cookie in Application_BeginRequest
or use the default culture define in the web.config:
I've also defined a ActionFilter (LanguageFilterAttribute) which injects the current culture in a base viewmodel so the client uses the current set on the server side.
An extended explanation can be found here.
Some more info about the globalize script and culture settings here.