Mvc/Jquery Validation Does Not Accept Comma as Decimal Separator

MVC/JQuery validation does not accept comma as decimal separator

Even though it was suggested, that this is rather a jQuery problem than an MVC problem, I think it is an MVC Problem.

No that is no correct. You seeing a client side validation error because, by default, jquery.validate.js (an independent 3rd party plugin not associated with MicroSoft, that MVC uses for client side validation) validates numbers based on the decimal separator being a . (dot), not a , (comma).

MVC is server side code and does not run in the browser. To perform client side validation, MVC's HtmlHelper methods that generate form controls render a set of data-val-* attributes in the html used to describe the validation to be performed, which are in turn parsed by the jquery.validate.unobtrusive.js plugin when the DOM is loaded, and uses those to add rules to the $.validator.

In the case of your double property it will add a data-val-number attribute (in addition to data-val-required attribute), which will add the number rule which is defined as

// http://docs.jquery.com/Plugins/Validation/Methods/number
number: function( value, element ) {
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
},

where the decimal separator is a dot, and the thousands separator is a comma (presumably because the plugin was developed in the US, so uses a US format).

You need to overwrite the default behavior which you can do by using plugins such as jquery.globalize, or including the following script (note the regex just swaps the dot and comma)

$.validator.methods.number = function (value, element) {
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:\.\d{3})+)?(?:,\d+)?$/.test(value);
}

Note the above script needs to be after the jquery.validate.js script but not wrapped in $(document).ready()

What links it to MVC for me, is the fact that I can solve the date validation problem by adding the line [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")] to the model.

Its actually your [DataType(DataType.Date)] attribute in conjunction with the [DisplayFormat] attribute that is influencing the html that is generated. The [DataType] attribute generates <input type="date" ... /> which in turn renders the browsers HTML-5 datepicker if the browser supports it. In accordance with the specifications the format must be yyyy-MM-dd (ISO format) hence the need for the [DisplayFormat] attribute as well.

The HTML-5 datepicker renders the date in the browsers culture. The image you have shown where the input is 26.1.2018 is because your browser culture is de-DE, but if I navigated to your site, I would see 26/1/2018 in the input because my culture is en-AU (Australian), and if a United States user navigated to your site, they would see 1/26/2018.

The reason client side validation works for the date property is that the jquery.validate.js plugin includes date rules for both US format (MM/dd/yyyy) and ISO format (yyyy-MM-dd).

And if you were to use @Html.TextBoxFor(m => m.Inbetriebnahmedatum) (which ignores your [DataType] and [DisplayFormat] attributes), and entered 26.1.2018 in the input, you would also see a client side validation error.

Allow dot and comma in numbers, not only for decimal

I just found this answer.

Fixing binding to decimals

It worked perfectly in my scenario. This guy solved the exactly same problem I was having!

I just had modify a few lines of code, but the most important part was this line:

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());

I modified it to accept nullable values.

MVC Jquery globalization - validating a decimal

So,

for the globalization issue, the solution was here and reflected in the edit of my question (so far seems to work perfectly).

as for the point/comma issue here is what i did

function validateFrNumber(e) {

theCulture = Globalize.cultureSelector;
foundFR = theCulture.indexOf("FR")
foundfr = theCulture.indexOf("fr")

if (foundFR != '-1' || foundfr != '-1') {
theValeur = $(e).find("input").val();
foundDot = theValeur.indexOf(".");

if (foundDot > -1) {
$(e).find("input").attr('value', theValeur.replace(".", ","));
}
}

}

and then call this in the input onkeyup:

<div class="input" onkeyup="validateFrNumber(this)">@Html.EditorFor(model => model.MydecimalValue)</div>

Issue with Decimals, Commas and Client-Side Validation

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:

<globalization enableClientBasedCulture="true" uiCulture="en-GB" culture="en-GB" />

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.



Related Topics



Leave a reply



Submit