6

I've been looking for a solution to this case for hours, and I did not find anything that worked out for me.

Here we use the comma for decimal separator, and when I send a value like "15,50", in my controller I get the value of the model as "1550", and it only makes sense if I send "15.50".

I covered the following topics, and nothing worked.

Set CultureInfo in Asp.net Core to have a . as CurrencyDecimalSeparator instead of ,

Aspnet Core Decimal binding not working on non English Culture

I am sending the form with ajax, using the $form.serializeArray() as below:

function getFormData(xform) {
    var $form = $('#' + xform);
    var unindexed_array = $form.serializeArray();
    var indexed_array = {};

    $.map(unindexed_array, function (n, i) {
        indexed_array[n['name']] = n['value'];
    });    
    return indexed_array;
}

send:

function PostFormAjax(controller, metodo, params, redir, divacao, reloadgrid) {

    if (params != null) {
        var formData = new FormData();

        var json = $.parseJSON(JSON.stringify(params));
        $(json).each(function (i, val) {
            $.each(val, function (k, v) {
                console.log(k + " : " + v);
                formData.append(k, v);
            });
        });
    }

    $.ajax({
        url: '/' + controller + '/' + metodo,
        data: JSON.stringify(params),
        contentType: 'application/json',
        type: 'POST',
        success: function (data) {
            AjaxFim(metodo, data, redir, divacao, reloadgrid);
        }
    });
}

My controller:

    [HttpPost]
    public IActionResult GravaProduto([FromBody] Produtos produto)
    {
        if (ModelState.IsValid)
        {
         //produto.Valorcusto is 1550 and not 15,50 as it was posted
         //produto.Valorvenda is 1550 and not 15,50 as it was posted
        }
    }

My Model.cs

public partial class Produtos
{
    public int Cod { get; set; }
    public string Descricao { get; set; }
    public decimal Valorcusto { get; set; }
    public decimal Valorvenda { get; set; }
}

I tried to make a replace in formData.append(k, v.replace(',', '.')); and also does not work, I also would not know which field is a decimal.

What do I have to do? because I'm lost, what should have been simpler became more complicated.

What I did to keep working until I found a solution was:

Wear a mask according to my regional setting:

$('.stValor').mask("#.###.##0,00", { reverse: true });

And before posting the form, I switch to the accepted formatting:

$('#fCadAltProd').validator().on('submit', function (e) {
    if (e.isDefaultPrevented()) {
    } else {
        e.preventDefault();
        $('.stValor').mask("#,###,##0.00", { reverse: true });
        //Ajax post here
    }
});
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
MariINova
  • 323
  • 4
  • 15
  • 1
    just write a custom model binder, instead of using culture info parse the string value as your decimal see this [article](https://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx/) – johnny 5 Jun 21 '18 at 21:32
  • Off topic, but you can [pass a `form` element to the `FormData` constructor](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData) and it will fill with all of your form elements automatically. – Heretic Monkey Jun 21 '18 at 23:10
  • `formData.append(k, v.replace(',', '.'))` should have worked if the value was "15,50". I could see it not working if the value was `1.150,50`, since you'd also have to replace `.` with `,`, maybe with something like `v.replace(/[,.]/g, (c) => c == "." ? "," : ".")`. – Heretic Monkey Jun 21 '18 at 23:17
  • You might want to look at [this answer to How can I parse a string with a comma thousand separator to a number?](https://stackoverflow.com/a/45309230). You'd still need to identify which are numbers, but that could be done with a simple regex like `/^[\d,.]+$/`. – Heretic Monkey Jun 21 '18 at 23:21

2 Answers2

5

Here we use the comma for decimal separator, and when I send a value like "15,50", in my controller I get the value of the model as "1550", and it only makes sense if I send "15.50".

Create a custom model binder using the Norwegian culture, because Norway also uses the comma decimal separator. You might also want to specify certain multiple allowed number styles.

Here is one that still needs to have error handling. It gives you the basic idea, though, and you can build on it from here. What I would do it copy most of the logic from the built-in DecimalModelBinder.

using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Logging;

namespace AspNetCorePlayground.Models
{
    public class MyFirstModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            var valueProviderResult = bindingContext
                .ValueProvider
                .GetValue(bindingContext.ModelName);

            var cultureInfo = new CultureInfo("no"); // Norwegian

            decimal.TryParse(
                valueProviderResult.FirstValue,
                // add more NumberStyles as necessary
                NumberStyles.AllowDecimalPoint, 
                cultureInfo,
                out var model);

            bindingContext
                .ModelState
                .SetModelValue(bindingContext.ModelName, valueProviderResult);

            bindingContext.Result = ModelBindingResult.Success(model);    
            return Task.CompletedTask;
        }
    }
}

Then decorate your class like this:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;

namespace AspNetCorePlayground.Models
{
    public class MyFirstModelBinderTest
    {
        [ModelBinder(BinderType = typeof(MyFirstModelBinder))]
        public decimal SomeDecimal { get; set; }
    }
}

I tested it on a controller:

[HttpGet("test")]
public IActionResult TestMyFirstModelBinder(MyFirstModelBinderTest model)
{
    return Json(model);
}

This is the result:

enter image description here

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
0

Use this plugin jquery.serializeToJSON and change your getFormData() function to

function getFormData(xform) {
    var $form = $('#' + xform);
    var obj = $form.serializeToJSON({
        parseFloat: {
            condition: ".stValor,.stQnt,.stDesconto",
            nanToZero: true,
            getInputValue: function (i) {
                return i.val().split(".").join("").replace(",", "."); 
            }
        }
    });
    return obj;
}

parseFloat.getInputValue: function(){}, By default, returns the input value without commas, not an error occurs in conversion. if your location uses comma for decimal separation, for example in German or Brazil, you can change to

function(i){ 
    return i.val().split(".").join("").replace(",", "."); 
}

This will do all the work for you.

Dark Ducke
  • 145
  • 1
  • 3
  • 12