小数,逗号和客户端验证问题

use*_*392 4 javascript validation jquery asp.net-mvc-4

我正在尝试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...
}
Run Code Online (Sandbox Code Playgroud)

我的jQueryFixes覆盖jquery.validate.jsrange()和的文件number():

$.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);
}
Run Code Online (Sandbox Code Playgroud)

......正如关于这个问题的许多帖子/问题所建议的那样(例如:这里这里).

很奇怪的是:

当我尝试提交一个像123,45这样的值时,即使我用Firebug调试了脚本并看到我的overrode函数被调用并返回true,我也无法提交表单.相反,我的EditorFor for decimal值因为任何原因而被集中,我似乎无法找出原因.

(我相信我的服务器端验证 - 使用自定义绑定器等 - 工作正常,这不是问题:我想要一些帮助,如何提交表单或为什么输入字段集中即使它看起来有效.)

编辑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")); 
...
Run Code Online (Sandbox Code Playgroud)

编辑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>
Run Code Online (Sandbox Code Playgroud)

...但我仍然面临同样的问题:validator.methods.number返回true但表单未提交,输入字段会被重点关注.

如果我在提交时检查输入元素,我可以看到它很快就会class="valid"发生class='input-validation-error',然后再返回valid.很奇怪.

结论:

@LeftyX为发现相同问题的人提供了一个非常好的完整解决方案.

我已经有了一个可以为空的小数的自定义模型绑定器,但全球化脚本和包含Model/ViewModel中的文化肯定会派上用场.

我的问题的另一个原因可能是我(偶然)包括一些脚本两次.

更新(2015年7月):

globalize.js现在有点不同了.参考.以这个答案文档的更新步骤.

Lef*_*tyX 7

我对此很挣扎.

对我来说最好的方法是为小数定义一个自定义绑定器:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

并将其绑定Application_StartGlobal.asax:

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
Run Code Online (Sandbox Code Playgroud)

我也用在全球化脚本(与文化),可以发现,在这里或下载的NuGet 这里.

你的包应该是这样的:

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"
    ));
Run Code Online (Sandbox Code Playgroud)

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

现在,当您的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));
}
Run Code Online (Sandbox Code Playgroud)

并自定义您的验证(我也添加了日期).你已经完成了jQueryFixes.

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

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

<globalization enableClientBasedCulture="true" uiCulture="en-GB" culture="en-GB" />
Run Code Online (Sandbox Code Playgroud)

我还定义了一个ActionFilter(LanguageFilterAttribute),它在基本视图模型中注入当前文化,以便客户端使用服务器端的当前集合.

可在此处找到扩展说明.

有关全球化脚本和文化设置的更多信息,请点击此处.