MVC/JQuery validation does not accept comma as decimal separator

22,682

Solution 1

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.

Solution 2

I think the problem is jquery validator i'v used thus for resolve the comma/point error

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

just try play with it

Solution 3

A more robust solution might be to wrap the validator methods in your own function which converts your comma separated number to one that is decimal separated.

One trick is to use .call to call the original validator function as if "this" was the original this that the developers thought (e.g. they use a function "this.optional" for their step validation).

var originalNumber = $.validator.methods.number;
var wrappedNumber = function (value, element) {
    var fixedValue = parseFloat(value.toString().replace(",", "."));
    return originalNumber.call($.validator.prototype, fixedValue, element);     // Call function as if "this" is the original caller
};
$.validator.methods.number = wrappedNumber;

You could make this work for any validator, e.g. the step validation:

var originalStep = $.validator.methods.step;
var wrappedStep = function (value, element, param) {
    var fixedValue = parseFloat(value.toString().replace(",", "."));
    return originalStep.call($.validator.prototype, fixedValue, element, param);
};
$.validator.methods.step = wrappedStep;
Share:
22,682

Related videos on Youtube

Mister 832
Author by

Mister 832

By Day I'm a Consultant for DWH / BI Technologies. In my sparetime I develop VB, C# and VBA applications

Updated on December 22, 2020

Comments

  • Mister 832
    Mister 832 over 3 years

    Update 07.01.2018

    Even though it was suggested, that this is rather a jQuery problem than an MS ASP MVC problem, I think it is an MVC Problem. I've created the whole app in asp.net core 2.0 MVC and the error persist. 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. Hence, MVC has an influence on the validation. So I would assume there is some way in MVC to fix this (See this post). Please post answers for asp.net core 2.0.

    Original Post

    In an MVC5 page I render a Double Property in a Textbox. When the page is loaded, the value is shown with a "," as decimal separator, which is correct, as the page runs on an German System. If I want to save the form, I get an validation error. How can this be solved? I know that there are some questions on the topic, but as far as i can see, most of them are outdated... I'm still struggling, that there is no setting or anything built-in that allows users from different countries to work with MVC apps.

    Model:

    [DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
    public Double Gewicht
    {
        get { return gewicht; }
        set { gewicht = value; OnPropertyChanged(new PropertyChangedEventArgs("Gewicht")); }
    }
    

    CSHTML:

    <div class="form-group">
        @Html.LabelFor(model => model.Gewicht, htmlAttributes: new { @class = "control-label col-md-3" })
        <div class="col-md-8">
            @Html.EditorFor(model => model.Gewicht, new { htmlAttributes = new { @class = "form-control col-md-1" } })
            @Html.ValidationMessageFor(model => model.Gewicht, "", new { @class = "text-danger" })
        </div>
    </div>
    

    Web.config

    <globalization uiCulture="de-DE" culture="de-DE" />
    

    Box after its loaded --> Value loaded with a comma as decimal separator enter image description here

    Box after submit button is clicked --> Validation error for the same value

    enter image description here

    Box after comma is changed to point --> No validation error enter image description here

    Update 05.01.2018

    I've tried the solution shown here which unfortunately doesn't work for me. However, I also discovered, that the values not only get not acceptet, but are also changed to numbers where group separator and decimal separator are mixed up (see picture). What happens is, that the value of 22 gets changed to 22.5 and stored in the database. The result beeing, that a value of 2,250.00 is stored to the database.

    enter image description here

    Update 07.01.2018

    What is also interesting, is the fact that the date fields accept the german format perfectly fine.

    Property

    private DateTime? inbetriebnahmedatum;
    
    [DataType(DataType.Date)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
    public DateTime? Inbetriebnahmedatum
    {
        get { return inbetriebnahmedatum; }
        set { inbetriebnahmedatum = value; OnPropertyChanged(new PropertyChangedEventArgs("Inbetriebnahmedatum")); }
    }
    

    The value shown below is accepted and processed without any errors. enter image description here

    Update 07.01.2018 - 2

    If I change the line in edit.cshtml from

    <input asp-for="Gewicht" class="form-control" />
    

    to

    <input name="Gewicht" id="Gewicht" type="number" class="form-control" value="@Model.Gewicht"/>
    

    The form can be submitted with the value "23,7" without validation errors. In the controller, the bound model property shows a value of "237", where as the IFormCollection shows a value of "23.7". This would suggest a problem with the model binder.

    • Mister 832
      Mister 832 over 6 years
      I don't think this is similar, since I use [DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)] and I'm fine with the format. Also I hope that there is a more "built in" way to solve this, since I can't imagine that MVC 5 doesn't allow to save decimal values in other formats than us-US.
    • Steve
      Steve over 6 years
      @Mister832 that has nothing to do with MVC5. The error you saw is produced by jquery validate javascript library. You can define a custom validator to handle comma
    • Mister 832
      Mister 832 over 6 years
      Thanks, I updated the title. I just thougt, that it has something to do with the data annotation in the base class.
    • Wurd
      Wurd over 6 years
      Perhaps use your own model binder if you haven't set your culture properly? stackoverflow.com/questions/793459/…
    • Mister 832
      Mister 832 over 6 years
      I don't think the culture is a problem, since the datepicker works fine, i.e. display in german date format is stored perfectly well.
    • Admin
      Admin over 6 years
      The is a client side issue, not a mvc issue. By default, the jquery.validate.js script validates numbers using a . (dot) as the decimal separator. You need to reconfigure the validator to use a , (comma) as a the decimal separator
    • Mister 832
      Mister 832 over 6 years
      I agree that this is a client side issue. However, I can fix the similar issue for date field with the annotaion [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")] (which btw I learned with the help of @StephenMuecke) in MVC. I would expect the same to be possible for decimal fields, as there are more ways to influence the clientside validation with MVC annotation, like max length, pattern aso.
    • Admin
      Admin over 6 years
      @Mister832, The datepicker works because the format is ISO format. The answer by Dans is one way to solve the issue (or you can use a plugin such as jquery.globalize). There is no way to do 'fix it' using server side code
    • Admin
      Admin over 6 years
      You might be interested in this plugin which displays and validates input for numeric types based on the server culture
  • Mister 832
    Mister 832 over 6 years
    Thanks, but as stated, the intention is to use the whole thing with out of the box functionalities. As cool as this "oneliner" looks, whats point of an MVC application, when I still have to fiddle around with things like that? Especially, when you watch the MVA videos, where they praise how easy web interface design becomes with an "model-only" approach...
  • blfuentes
    blfuentes about 5 years
    Like this solution but it is important to point out that parseFloat is not enough for the number transformation as for example parseFloat("50-ba") returns 5
  • Peheje
    Peheje about 5 years
    Thanks for your comment. As with almost anything in programming, the answer is "it depends". We pursued a different approach mimicking <input type="number"> for <input type="text" class="number-input"> but for danish locale by limiting the input to 0-9 and one "," character with js.