I am trying to have my client side validation (model binding) to support different cultures, and I found an interesting blog on the subject on which I am trying to implement.
http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx
Poco
public class Jogador
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
}
I've got my custom DecimalModelBinder class
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
{
actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
My web.config:
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages"/>
</namespaces>
</pages>
Global.asax are altered to use my custom ModelBinder on decimal and decimal? values
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Still the client-side validation fails on decimal entered in my view with a "," as a decimal separator. It does not handle both "," and ".". The js validation does not seem to take my custom binding in consideration
Reading the blog article over and over again, I just can't seem to figure out what I am missing.
Here is the view:
@model MVC_Empty.Web.Models.Jogador
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Jogador</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Salary)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Salary)
@Html.ValidationMessageFor(model => model.Salary)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Server side validation seems fine, but how to handle the client-side validation in order to send a POST when clicking the submit button.
The javascript validation does not handle the comma.
Finally, by understanding that the Custom DecimalModelBinder would only handle server-side validation and not affect the jquery.validate.js which handles the client-side validation, I found a solution to the problem.
Extending the validation solved my issue.
Extend the validation by a new .js file as a workaround to the problem:
$.validator.methods.number = function(value, element) {
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/ .test(value);
};
This blog was really helpful http://rebuildall.umbraworks.net/2011/03/02/jQuery_validate_and_the_comma_decimal_separator
Try this it's slightly modified version:
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
{
//if with period use InvariantCulture
if (valueResult.AttemptedValue.Contains("."))
{
actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
CultureInfo.InvariantCulture);
}
else
{
//if with comma use CurrentCulture
actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
CultureInfo.CurrentCulture);
}
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
It works for me.
UPDATE
If you're using a partial view use $.validate.unobtrusive.parse("#selector")
to reapply validation. If not try setting a globalization in your web.config (one in the root, not in the Views) to something like this:
<configuration>
<system.web>
<globalization fileEncoding="utf-8"
requestEncoding="utf-8"
responseEncoding="utf-8"
culture="hr-HR" uiCulture="hr-HR" />
</system.web>
</configuration>
I have globalization set to Hr where we use comma as delimiters but with the DecimalModelBinder i have posted it will properly parse decimal or comma. Regards
I've just fixed this issue replacing the line 1050 of the jquery.validate.js with this code:
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:[.|\,]\d+)?$/.test(value);
I've just changed smth on the regular expression used to validate the number and now it's working great!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With