MVC 3 client side validation, model binding decima

2019-01-22 08:45发布

问题:

I am trying to have my client side validation (model binding) to support different cultures, and I found an interesting blog on the subject on which I am trying to implement.

http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx

Poco

  public class Jogador
  {
    public int ID { get; set; }

    public string Name { get; set; }

    public decimal Salary { get; set; }
  }

I've got my custom DecimalModelBinder class

  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
      {
        actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
      }
      catch (FormatException e)
      {
        modelState.Errors.Add(e);
      }

      bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
      return actualValue;
    }
  }

My web.config:

<compilation debug="true" targetFramework="4.0">
  <assemblies>
    <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  </assemblies>
</compilation>

<authentication mode="Forms">
  <forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

<pages>
  <namespaces>
    <add namespace="System.Web.Helpers" />
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Routing" />
    <add namespace="System.Web.WebPages"/>
  </namespaces>
</pages>

Global.asax are altered to use my custom ModelBinder on decimal and decimal? values

protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();

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

  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
}

Still the client-side validation fails on decimal entered in my view with a "," as a decimal separator. It does not handle both "," and ".". The js validation does not seem to take my custom binding in consideration

Reading the blog article over and over again, I just can't seem to figure out what I am missing.

Here is the view:

@model MVC_Empty.Web.Models.Jogador

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Jogador</legend>

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

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

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Server side validation seems fine, but how to handle the client-side validation in order to send a POST when clicking the submit button.

The javascript validation does not handle the comma.

回答1:

Finally, by understanding that the Custom DecimalModelBinder would only handle server-side validation and not affect the jquery.validate.js which handles the client-side validation, I found a solution to the problem.

Extending the validation solved my issue.

Extend the validation by a new .js file as a workaround to the problem:

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

This blog was really helpful http://rebuildall.umbraworks.net/2011/03/02/jQuery_validate_and_the_comma_decimal_separator



回答2:

Try this it's slightly modified version:

 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
            {
                //if with period use InvariantCulture
                if (valueResult.AttemptedValue.Contains("."))
                {
                    actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
                    CultureInfo.InvariantCulture);
                }
                else
                {
                    //if with comma use CurrentCulture
                    actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
                    CultureInfo.CurrentCulture);
                }

            }
            catch (FormatException e)
            {
                modelState.Errors.Add(e);
            }

            bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
            return actualValue;
        }
    }

It works for me.

UPDATE

If you're using a partial view use $.validate.unobtrusive.parse("#selector") to reapply validation. If not try setting a globalization in your web.config (one in the root, not in the Views) to something like this:

<configuration>
  <system.web>
    <globalization fileEncoding="utf-8" 
                    requestEncoding="utf-8" 
                    responseEncoding="utf-8" 
                    culture="hr-HR" uiCulture="hr-HR" />
  </system.web>
</configuration>

I have globalization set to Hr where we use comma as delimiters but with the DecimalModelBinder i have posted it will properly parse decimal or comma. Regards



回答3:

I've just fixed this issue replacing the line 1050 of the jquery.validate.js with this code:

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

I've just changed smth on the regular expression used to validate the number and now it's working great!!