1

I have field for decimal price, for instance 20,00 and want to write my own validation message instead of default "The field Price must be a number". As I soon have understood, I get validation error because of comma in number, view interprets it as a string. So that I should fix this as well.

I found this solution written by @DarinDimitrov, I write custom model binder

public class PriceModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var displayFormat = bindingContext.ModelMetadata.DisplayFormatString;
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (!string.IsNullOrEmpty(displayFormat) && value != null)
        {
            decimal price;
            if (decimal.TryParse(value.AttemptedValue, NumberStyles.AllowDecimalPoint, CultureInfo.CurrentCulture, out price))
            {
                return price;
            }
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Неверный формат числа");
        }
        return base.BindModel(controllerContext, bindingContext);
    }
}

I also add it in Global.asax

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    ModelBinders.Binders.Add(typeof(decimal), new PriceModelBinder());
}

Besides I add to web.config <globalization culture="ru-RU" uiCulture="ru-RU" /> in <system.web>. When I place a breakpoint and try to add entity from view, but nothing changes, I get the same default error string. Moreover breakpoint wasn't caught.

After a little more search I found that model binder works only when POST-method is calling, but my method is already POST as I add entity. I tried to add check in controller ModelState.IsValid, but it has no effect, as form wasn't sent to server. Here my ideas is over, maybe someone can help. Down I write parts of model, view and controller.

public class ServiceDto
{
    //other properties
    [Display(Name = "Цена"), DataType(DataType.Currency, ErrorMessage = "Неверный формат числа")]
    [Required(ErrorMessage = "Цена услуги обязательна")]
    public decimal Price { get; set; }
}

@Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })

public class OperatorController : Controller
{
    [HttpPost]
    public ActionResult CreateService(ServiceDto serviceDto)
    {
        //mapping here
        _unitOfWork.Services.Create(service);
        _unitOfWork.Save();
        return RedirectToAction("Index");
    }
}

UPDATE

I add this js script to view after all other ones, but nothing changed.

<script src="/Scripts/jquery-1.10.2.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="/Scripts/bootstrap.js"></script>
<script src="/Scripts/respond.js"></script>
<script type="text/javascript">
    $.validator.methods.number = function (value, element) {
        return this.optional(element) || /^-?(?:d+|d{1,3}(?:[s.,]d{3})+)(?:[.,]d+)?$/.test(value);
    };
</script>

Maybe this script is not being executed? Sorry, I mostly work with backend, and if problem is connected with js, it is dark forest for me.

QuarK
  • 1,162
  • 2
  • 12
  • 27
  • Sound like you getting a client side validation error if the form is not submitting. You need to overwrite the `$.validator` (which by default validates numbers with a `.` (dot) as the decimal separator) –  Oct 21 '17 at 02:15
  • @StephenMuecke Thanks I'll try this. But it sounds strange, if `$.validator` stops sending form, then what for model binder is. Maybe client side validation turn off by default, I enable it to validate email. – QuarK Oct 21 '17 at 02:26
  • No, they are completely separate things. Client side validation is javascipt code running on the client. The model binder is server side code. And note that if you have set the culture in the `web.config` file to one that expects a comma as the decimal separator, then you do not need your custom model binder –  Oct 21 '17 at 02:29
  • Add the following script in your view (after you have loaded the jQuery validate scripts, and not in `$document.ready()`) - `$.validator.methods.number = function(value, element) { return this.optional(element) || /^-?(?:d+|d{1,3}(?:[s.,]d{3})+)(?:[.,]d+)?$/.test(value);};`. You also need to override the `range` validation (can't remember what that is just now) –  Oct 21 '17 at 02:33
  • @StephenMuecke I fought the same way, when I set culture, but [view](https://pp.userapi.com/c841122/v841122668/2d8d1/zVG93BEpQ-8.jpg) thinks another way. Thanks for the js, will certain try it later. want to sleep now, 5:40 a. m.:) – QuarK Oct 21 '17 at 02:38
  • That's because client side validation use `jquery.validate.js` which runs on the client and by default it validates numbers with a dot as the decimal separator –  Oct 21 '17 at 02:43
  • @StephenMuecke I try yours script, but problem remains, please, see update. – QuarK Oct 21 '17 at 17:17

1 Answers1

2

As Stephen Muecke said, by default jquery.validate.js uses decimal point as separator for numeric inputs (this is a standard to comply with JS numeric format). To include decimal comma as in Russian culture, you need to override both jquery.validator.methods.number and jquery.validator.methods.range behavior as given below:

// taken from Lenard Gunda (see reference section)
$.validator.methods.number = function (value, element) {
    // notice additional backslashes to escape formats
    return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/.test(value);
}

$.validator.methods.range = function (value, element, param) {
    var globalizedValue = value.replace(",", ".");
    return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
}

If you're using Globalize plugin, you can use parseFloat from that plugin to replace regex:

Globalize.culture('ru-RU');

function parseNumber(value) {
    return Globalize.parseFloat(value);
}

$.validator.methods.number = function (value, element) {
    return this.optional(element) || $.isNumeric(parseNumber(value));
}

$.validator.methods.range = function (value, element, param) {
    var globalizedValue = parseNumber(value);
    return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
}

References:

jQuery validate and the comma decimal separator

Issue with Decimals, Commas and Client-Side Validation

jQuery Validation Plugin: validate decimal number with comma as decimal separator

Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61
  • Thanks for whole answer) I have already fix this, thinking about answer question myself. Btw, code from UPDATE didn't work because of `;`, because it is JS. – QuarK Oct 24 '17 at 01:28