【问题标题】:Issue with Decimals, Commas and Client-Side Validation小数、逗号和客户端验证的问题
【发布时间】:2013-12-14 08:26:34
【问题描述】:

我正在尝试为 nullable<decimal> 实现客户端验证,其小数分隔符可以是逗号(例如:123,45)。

在我看来:

...

<div class="editor-label">
    @Html.LabelFor(model => model.Turnover)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.Turnover)
    @Html.ValidationMessageFor(model => model.Turnover)
</div>

...

@section Scripts {

   @Styles.Render("~/Content/themes/base/css")
   @Scripts.Render("~/bundles/jquery")
   @Scripts.Render("~/bundles/jqueryui")
   @Scripts.Render("~/bundles/jqueryval")
   @Scripts.Render("~/bundles/jQueryFixes")
   ...scripts for this view...
}

我的 jQueryFixes 为 range()number() 覆盖了 jquery.validate.js 文件:

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

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

...正如许多关于此问题的帖子/问题中所建议的那样(例如:herehere)。

很奇怪:

当我尝试提交诸如 123,45 之类的值时,即使我已经使用 Firebug 调试了脚本并看到我的覆盖函数被调用并返回 true,我也无法提交表单.相反,无论出于何种原因,我的十进制值的 EditorFor 都被关注,我似乎无法找出原因。

(我相信我的服务器端验证 - 使用自定义活页夹等 - 工作正常,并且 不是这里的问题:我想要一些有关如何获取表单的帮助提交或为什么输入字段得到焦点,即使它看起来有效。)

编辑 1:

附加信息。在我的 BundlesConfig.cs 中:

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-{version}.js"));
...
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                    "~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                    "~/Scripts/jquery.unobtrusive*",
                    "~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/jQueryFixes").Include(
                    "~/Scripts/jQueryFixes.js")); 
...

编辑 2:

在@LeftyX 建议之后,我尝试使用 Globalize 脚本(在删除我的 jQueryFixes.js 之后):

<script type="text/javascript">
    ...

    $( document ).ready(function() {
        Globalize.culture("en-US", "pt-PT");
    });

    $.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]);
        }
    });

   ...
</script>

...但我仍然面临同样的问题:validator.methods.number 返回 true,但未提交表单,而是输入字段获得焦点。

如果我在提交时检查输入元素,我可以看到它很快从class="valid" 变为class='input-validation-error',然后又回到valid。很奇怪。

结论:

@LeftyX 为遇到相同问题的人提供了一个非常好的和完整的解决方案。

我已经为可空小数创建了一个自定义模型绑定器,但是全球化脚本和在模型/视图模型中包含文化肯定会派上用场。

我的问题的另一个原因可能是我(不小心)两次包含一些脚本。

更新(2015 年 7 月):

globalize.js 现在有点不同了。参考。到this answerthe documentation 获取更新的步骤。

【问题讨论】:

    标签: javascript jquery validation asp.net-mvc-4


    【解决方案1】:

    我在这方面遇到了很多困难。

    对我来说最好的方法是为小数定义一个自定义活页夹:

    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;
        }
    }
    

    并将其绑定到Application_Start in Global.asax:

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

    我还使用可以在 here 找到或从 nuget here 下载的 globalize 脚本(带有文化)。

    您的捆绑包应如下所示:

    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"
        ));
    

    当然,如果你想支持不同的本地化,你可以添加更多的文化。

    现在,当您的 DOM 准备就绪(javascript)时,您可以定义您的文化:

    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));
    }
    

    并自定义您的验证(我也添加了日期)。你已经在 jQueryFixes 中做到了。

    您可以找到一个工作示例 here (MvcAppForDecimals),您可以在其中更改语言 使用工具栏和 cookie,以便服务器上的文化也可以更改。

    在示例中,我读取了 Application_BeginRequest 中的 cookie 或使用 web.config 中定义的默认区域性:

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

    我还定义了一个 ActionFilter (LanguageFilterAttribute),它将当前区域性注入基本视图模型中,以便客户端使用服务器端的当前设置。

    扩展说明见here

    有关全球化脚本和文化设置here 的更多信息。

    【讨论】:

    • 嘿。非常好的和完整的答案,谢谢。我已经有了我的 DecimalModelBinder 但这不是问题,因为我正在努力进行客户端验证(即表单甚至没有提交到服务器,以便 ModelBinder 可以完成它的工作)。不过,我已经添加了 Globalize 脚本,但我仍然面临同样的问题:好像验证正在接受该值,但其他东西阻止了它被提交。
    • 谢谢。问题是服务器和客户端可能使用不同的文化。 Globalize 脚本和 DecimalModelBinder 允许您避免将逗号或点作为小数检查,但您必须设置要使用的区域性。你可以看看我的示例代码。
    • 现在这么简单的事情要做很多工作!您的工作示例也有很大帮助。谢谢并点赞。
    • @user1987392:很高兴我能帮上忙。干杯。
    【解决方案2】:

    只是关于globalize.js 的一个小更新。现在情况有点不同(并且令人困惑):

    包括以下脚本:

    <!--
    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>
    

    现在我们需要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>
    

    I18n 内容以 JSON here 的形式提供。

    如果您在getJSON() 上收到 404,请记得添加:

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

    在您的 Web.config(ASP .NET MVC 应用程序)中。

    【讨论】:

    • //修复范围以使用全球化方法有问题。执行此代码后,客户端验证不起作用。浏览器自动提交表单到服务器,无需客户端验证。我不知道为什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多