Issue with Decimals, Commas and Client-Side Validation

前端 未结 2 617
栀梦
栀梦 2021-01-13 18:27

I\'m trying to achieve client-side validation for a nullable whose decimal separator can be a comma (e.g.: 123,45).

In my View:



        
相关标签:
2条回答
  • 2021-01-13 19:13

    Just a small update regarding globalize.js. Things are a bit different (and confusing) now:

    Include the scripts as follows:

    <!--
    First, we load Globalize's dependencies (`cldrjs` and its supplemental
    module).
    -->
    <script type="text/javascript" src="~/Scripts/cldr.js"></script>
    <script type="text/javascript" src="~/Scripts/cldr/event.js"></script>
    <script type="text/javascript" src="~/Scripts/cldr/supplemental.js"></script>
    
    <!--
    Next, we load Globalize and its modules.
    -->
    <script type="text/javascript" src="~/Scripts/globalize.js"></script>
    <script type="text/javascript" src="~/Scripts/globalize/number.js"></script>
    

    Now we need to load the I18n content onto Globalize:

    <script type='text/javascript'>
    
        // At this point, we have Globalize loaded. But, before we can use it, we
        // need to feed it on the appropriate I18n content (Unicode CLDR). In order
        // to do so, we use `Globalize.load()` and pass the content.
    
        $.when($.getJSON('/Scripts/cldr/supplemental/likelySubtags.json'),
                   $.getJSON('/Scripts/cldr/main/en-GB/numbers.json'),
                   ...other locales...
                   $.getJSON('/Scripts/cldr/supplemental/numberingSystems.json'))
            .done(function (result1, result2, result3, result4) {
                Globalize.load(result1[0]); //contains data of first executed request
                ...load the other ones...
                Globalize.load(result3[0]); //contains data of third executed request
                Globalize.load(result4[0]); //contains data of fourth executed request
    
                var glob = Globalize([YOUR-LOCALE]); // e.g. en-UK, pt-PT, pt-BR, es-ES, etc.
    
                $.validator.methods.number = function (value, element) {
                    var number = glob.parseNumber(value);
                    return this.optional(element) || jQuery.isNumeric(number);
                }
    
                //Fix the range to use globalized methods
                jQuery.extend(jQuery.validator.methods, {
                    range: function (value, element, param) {
                        var val = glob.formatNumber(value);
                        return this.optional(element) || (val >= param[0] && val <= param[1]);
                    }
    
            });
        });
    
    </script>
    

    The I18n content is available as JSON here.

    If you get 404 on your getJSON(), remember to add:

    <system.webServer>
    
        (...)
    
        <staticContent>
          <mimeMap fileExtension=".json" mimeType="application/json" />
        </staticContent>
    
    </system.webServer>
    

    in your Web.config (ASP .NET MVC application).

    0 讨论(0)
  • 2021-01-13 19:26

    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.

    0 讨论(0)
提交回复
热议问题