Long story short, I started a new project using MVC 5.2.3 (with all updated JS frameworks) expecting that many of the issues I had with validations from MVC 2 were solved, how wrong was I.
Basically I am driving myself crazy trying to provide validation to DateTime
and Decimal
.
With the decimal field, using browsers in EN and DE (cultures) I am getting problems with comma and point (decimal division) and with the range that I am setting.
With the DateTime
I even tried a DisplayFormat
just to display the Date and not the time, and anyway the order of day/month/year with separations with .
or /
or -
are simply failing.
Examples:
The same applies to the datetime display format and similar JS validations....
I share with you guys what I have at the moment:
TestObject
public class TestObject
{
....
[Display(Name = "D2", ResourceType = typeof(WebApplication1.Models.Res.TestObject))]
[Range(0, 999.9999)]
public decimal? D1 { get; set; }
// On DBContext I have defined the range/precision
// modelBuilder.Entity<TestObject>().Property(x => x.D2).HasPrecision(7, 4);
[Display(Name = "Date1", ResourceType = typeof(WebApplication1.Models.Res.TestObject))]
//[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = @"{0:dd\/MM\/yyyy}")]
public DateTime Date1 { get; set; }
....
}
view.cshtml
<div class="form-group">
@Html.LabelFor(model => model.D2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.D2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.D2, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Date1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Date1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Date1, "", new { @class = "text-danger" })
</div>
</div>
_layout.cshtml
<script src="~/Scripts/globalize/globalize.js" type="text/javascript"></script></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.de.js" type="text/javascript"></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.en-US.js" type="text/javascript"></script>
<script>
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$.validator.methods.date = function (value, element) {
return this.optional(element) ||
Globalize.parseDate(value);
}
$(document).ready(function () {
Globalize.culture('@System.Threading.Thread.CurrentThread.CurrentCulture');
});
</script>
DecimalModelBinder
public class DecimalModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
object result = null;
// Don't do this here!
// It might do bindingContext.ModelState.AddModelError
// and there is no RemoveModelError!
//
// result = base.BindModel(controllerContext, bindingContext);
string modelName = bindingContext.ModelName;
string attemptedValue =
bindingContext.ValueProvider.GetValue(modelName).AttemptedValue;
// Depending on CultureInfo, the NumberDecimalSeparator can be "," or "."
// Both "." and "," should be accepted, but aren't.
string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
string alternateSeperator = (wantedSeperator == "," ? "." : ",");
if (attemptedValue.IndexOf(wantedSeperator) == -1
&& attemptedValue.IndexOf(alternateSeperator) != -1)
{
attemptedValue =
attemptedValue.Replace(alternateSeperator, wantedSeperator);
}
try
{
if (bindingContext.ModelMetadata.IsNullableValueType
&& string.IsNullOrWhiteSpace(attemptedValue))
{
return null;
}
result = decimal.Parse(attemptedValue, NumberStyles.Any);
}
catch (FormatException e)
{
bindingContext.ModelState.AddModelError(modelName, e);
}
return result;
}
}
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
...
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string culture = null;
if (context.Request.UserLanguages != null && Request.UserLanguages.Length > 0)
{
culture = Request.UserLanguages[0];
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
}
}
web.config
<globalization culture="auto" uiCulture="auto" enableClientBasedCulture="true" />
It seems that I also needed to create my own range method..... Now it seems to be working.
$.validator.methods.range = function(value, element, param) {
console.log('----------------');
console.log(value);
console.log(element);
var globalizedValue = value.replace(",", ".");
console.log(param[0]);
var globalizedValueParam0 = param[0];
console.log(param[1]);
var globalizedValueParam1 = param[1];
console.log(globalizedValueParam0);
console.log(globalizedValueParam1);
//return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
return this.optional(element) || (globalizedValue >= globalizedValueParam0 && globalizedValue <= globalizedValueParam1);
};