Question
The below code is working fine Server side and not Client side. Why ?
When I submit the form, control goes to BeAValidDate function to check the date is valid or not. Is there any way to Validate the date without going to server using Fluent Validation?
Scripts
<script src="jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="jquery.validate.js" type="text/javascript"></script>
<script src="jquery.validate.unobtrusive.js" type="text/javascript"></script>
Model
public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(x => x.FromDate)
.NotEmpty()
.WithMessage("Date is required!")
.Must(BeAValidDate)
.WithMessage("Invalid Date");
}
private bool BeAValidDate(String value)
{
DateTime date;
return DateTime.TryParse(value, out date);
}
}
Controller
public class PersonController : Controller
{
public ActionResult Index()
{
return View(new Person { FromDate = DateTime.Now.AddDays(2).ToString()});
}
[HttpPost]
public ActionResult Index(Person p)
{
return View(p);
}
}
View
#using (Html.BeginForm("Index", "Person", FormMethod.Post))
{
#Html.LabelFor(x => x.FromDate)
#Html.EditorFor(x => x.FromDate)
#Html.ValidationMessageFor(x => x.FromDate)
<input type="submit" name="Submit" value="Submit" />
}
Trick using Greater Then Or Equal To Validator. Works for me.
Global.asax - Application Start Event
FluentValidationModelValidatorProvider.Configure(x =>
{
x.Add(typeof(GreaterThanOrEqualValidator),
(metadata, Context, rule, validator) =>
new LessThanOrEqualToFluentValidationPropertyValidator
(
metadata, Context, rule, validator
)
);
});
Model
[Validator(typeof(MyViewModelValidator))]
public class MyViewModel
{
[Display(Name = "Start date")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}",
ApplyFormatInEditMode = true)]
public DateTime StartDate { get; set; }
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}",
ApplyFormatInEditMode = true)]
public DateTime DateToCompareAgainst { get; set; }
}
Rule
public class MyViewModelValidator : AbstractValidator<MyViewModel>
{
public MyViewModelValidator()
{
RuleFor(x => x.StartDate)
.GreaterThanOrEqualTo(x => x.DateToCompareAgainst)
.WithMessage("Invalid start date");
}
}
FluentValidationPropertyValidator
public class GreaterThenOrEqualTo : FluentValidationPropertyValidator
{
public GreaterThenOrEqualTo(ModelMetadata metadata,
ControllerContext controllerContext,
PropertyRule rule,
IPropertyValidator validator)
: base(metadata, controllerContext, rule, validator)
{
}
public override IEnumerable<ModelClientValidationRule>
GetClientValidationRules()
{
if (!this.ShouldGenerateClientSideRules())
{
yield break;
}
var validator = Validator as GreaterThanOrEqualValidator;
var errorMessage = new MessageFormatter()
.AppendPropertyName(this.Rule.GetDisplayName())
.BuildMessage(validator.ErrorMessageSource.GetString());
var rule = new ModelClientValidationRule{
ErrorMessage = errorMessage,
ValidationType = "greaterthanorequaldate"};
rule.ValidationParameters["other"] =
CompareAttribute.FormatPropertyForClientValidation(
validator.MemberToCompare.Name);
yield return rule;
}
}
Controller Action Method
public ActionResult Index()
{
var model = new MyViewModel
{
StartDate = DateTime.Now.AddDays(2),
DateToCompareAgainst = default(DateTime) //Default Date
};
return View(model);
}
[HttpPost]
public ActionResult Index(Practise.Areas.FluentVal.Models.MyViewModel p)
{
return View(p);
}
View
#using (Html.BeginForm("Index", "Person", FormMethod.Post,
new { id = "FormSubmit" }))
{
#Html.Hidden("DateToCompareAgainst", Model.DateToCompareAgainst);
#Html.LabelFor(x => x.StartDate)
#Html.EditorFor(x => x.StartDate)
#Html.ValidationMessageFor(x => x.StartDate)
<button type="submit">
OK</button>
}
Script
<script src="jquery-1.4.1.min.js" type="text/javascript"></script>
<script src="jquery.validate.js" type="text/javascript"></script>
<script src="jquery.validate.unobtrusive.js" type="text/javascript"></script>
<script type="text/javascript">
(function ($) {
$.validator.unobtrusive.adapters.add('greaterthanorequaldate',
['other'], function (options) {
var getModelPrefix = function (fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
};
var appendModelPrefix = function (value, prefix) {
if (value.indexOf("*.") === 0) {
value = value.replace("*.", prefix);
}
return value;
}
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(":input[name=" + fullOtherName +
"]")[0];
options.rules['greaterthanorequaldate'] = element;
if (options.message != null) {
options.messages['greaterthanorequaldate'] = options.message;
}
});
$.validator.addMethod('greaterthanorequaldate',
function (value, element, params) {
var date = new Date(value);
var dateToCompareAgainst = new Date($(params).val());
if (isNaN(date.getTime()) || isNaN(dateToCompareAgainst.getTime())) {
return false;
}
return date >= dateToCompareAgainst;
});
})(jQuery);
</script>
There are things I don't understand about your settings and about what is not working. Do you mean the verification that the date must be valid is not working, or that the fact the date is required is not working?
Fluent validation is able to emit code for unobtrusive validation too, so the required constraint should work properly.
The fact that the date is valid is completely another story. If you specify your FromDate as a DateTime (have you declared it as a DateTime or as a string?) the verification of correctness of date is AUTOMATICALLY performed by other validators included in the Mvc framework, so you dont need to repeat it in the fluent validation rules. However, before Mvc4 this validation check were performed ONLY on the server side. With Mvc 4 the Asp.net Mvc Team fixed this problem and extended the check also on the client side.
However, on the client side, everything work propery only for the en-US date format, , since unobtrusive validation doesn't handle globalization. If you need date in other cultures you need to use the Globalize library, and you need to set up globalization on the client side. If you are interested to globalization you may see this post of my blog.
To add automatic Date correction also to Mvc 3. You must define an extended ClientDataTypeModelValidatorProvider like the one of Mvc4. Below the code:
public class ClientDataTypeModelValidatorProviderExt : ClientDataTypeModelValidatorProvider
{
public static Type ErrorMessageResources { get; set; }
public static string NumericErrorKey { get; set; }
public static string DateTimeErrorKey { get; set; }
private static readonly HashSet<Type> _numericTypes = new HashSet<Type>(new Type[] {
typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort),
typeof(int), typeof(uint),
typeof(long), typeof(ulong),
typeof(float), typeof(double), typeof(decimal)
});
private static bool IsNumericType(Type type)
{
Type underlyingType = Nullable.GetUnderlyingType(type); // strip off the Nullable<>
return _numericTypes.Contains(underlyingType ?? type);
}
internal sealed class NumericModelValidator : ModelValidator
{
public NumericModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
: base(metadata, controllerContext)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
ModelClientValidationRule rule = new ModelClientValidationRule()
{
ValidationType = "number",
ErrorMessage = MakeErrorString(Metadata.GetDisplayName())
};
return new ModelClientValidationRule[] { rule };
}
private static string MakeErrorString(string displayName)
{
// use CurrentCulture since this message is intended for the site visitor
return String.Format(CultureInfo.CurrentCulture, ErrorMessageResources.GetProperty(NumericErrorKey, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).GetValue(null, null) as string, displayName);
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// this is not a server-side validator
return Enumerable.Empty<ModelValidationResult>();
}
}
internal sealed class DateTimeModelValidator : ModelValidator
{
public DateTimeModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
: base(metadata, controllerContext)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
ModelClientValidationRule rule = new ModelClientValidationRule()
{
ValidationType = "globalizeddate",
ErrorMessage = MakeErrorString(Metadata.GetDisplayName())
};
return new ModelClientValidationRule[] { rule };
}
private static string MakeErrorString(string displayName)
{
// use CurrentCulture since this message is intended for the site visitor
return String.Format(CultureInfo.CurrentCulture, ErrorMessageResources.GetProperty(DateTimeErrorKey, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).GetValue(null, null) as string, displayName);
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// this is not a server-side validator
return Enumerable.Empty<ModelValidationResult>();
}
}
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
{
if (metadata == null)
{
throw new ArgumentNullException("metadata");
}
if (context == null)
{
throw new ArgumentNullException("context");
}
List<ModelValidator> res = null;
if (NumericErrorKey == null || ErrorMessageResources == null)
res = base.GetValidators(metadata, context).ToList();
else
{
res = new List<ModelValidator>();
if (IsNumericType(metadata.ModelType))
{
res.Add(new NumericModelValidator(metadata, context));
}
}
if ( (metadata.ModelType == typeof(DateTime) || metadata.ModelType == typeof(DateTime?)))
{
if(ErrorMessageResources != null && DateTimeErrorKey != null)
res.Add(new DateTimeModelValidator(metadata, context));
}
return res;
}
}
Then in the global.asax you must substititute the standard one with this:
var old = ModelValidatorProviders.Providers.Where(x => x is ClientDataTypeModelValidatorProvider).FirstOrDefault();
if (old != null) ModelValidatorProviders.Providers.Remove(old);
ModelValidatorProviders.Providers.Add(new ClientDataTypeModelValidatorProviderExt());
Now you have to add the javascript snipped that do the control on the client side:
$.validator.addMethod(
"globalizeddate",
function (value, element, param) {
if ((!value || !value.length) && this.optional(element)) return true; /*success*/
var convertedValue = Globalize.parseDate(value);
return !isNaN(convertedValue) && convertedValue;
},
"field must be a date/time"
);
There I used the Globalize function to verify correct date. Install it. It is the only way to have a date frormat compatible with the .net format. Moreover it works in all .net cultures. Using a standard javascript date parsing, is not compatible with the formats accepted by .Net in some browsers.
In MVC 3 below code should work fine.
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<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("Index", "Person", FormMethod.Post))
{
#Html.LabelFor(x => x.FromDate)
#Html.EditorFor(x => x.FromDate)
#Html.ValidationMessageFor(x => x.FromDate)
<input type="submit" name="Submit" value="Submit" />
}
Simple working Example in MVC 4
_Layout.cshtml:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title - My ASP.NET MVC Application</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div id="body">
#RenderSection("featured", required: false)
<section class="content-wrapper main-content clear-fix">
#RenderBody()
</section>
</div>
#Scripts.Render("~/bundles/jquery")
#RenderSection("scripts", required: false)
</body>
</html>
View:
#model Mvc4Test.Models.Person
#{
ViewBag.Title = "test";
}
<h2>test</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Part</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>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
For more details.
Related
I have two models, LoginModel and DatabaseModel. Combining them, I have created DatabaseCombinedWithOtherModel. The View, Login.cshtml is Strongly-Typed with the combined model. On running the Login.cshtml, the LoginModel returns null
I have all the necessary get and set methods
Here is the Controller class
namespace ReadingCat.Controllers
{
public class LoginController : Controller
{
private int userid;
// GET: Login
[HttpGet]
public ActionResult Login()
{
return View(new DatabaseCombinedWithOtherModel());
}
[HttpPost]
public ActionResult Login(DatabaseCombinedWithOtherModel model)
{
string realPassword = "";
string paswordFromUser = "";
string query = "SELECT password, userid FROM USERS WHERE username
= '" + model.loginModel.username + "'";
DataSet dataSet = model.databaseModel.selectFunction(query);
if (realPassword == paswordFromUser)
{
userid =
Convert.ToInt32(dataSet.Tables[0].Rows[0].ItemArray[1]);
model.loginModel.userid = userid;
return View("~/Views/Profile/Profile.cshtml",
model.loginModel);
}
else
return View();
}
}
}
Here is the Model:
namespace ReadingCat.Models
{
public class DatabaseCombinedWithOtherModel
{
public DatabaseModel databaseModel { get; set; }
public LoginModel loginModel { get; set; }
}
}
And here is the View:
#model ReadingCat.Models.DatabaseCombinedWithOtherModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Login</title>
<!-- Main css -->
<link rel="stylesheet" href="~/css/login.css">
</head>
<body>
<div class="login-page">
<div class="form">
<form class="register-form">
</form>
#using (Html.BeginForm("Login", "Login", FormMethod.Post))
{
<form class="login-form">
#Html.TextBox("Username", null, new { placeholder = "Username",
#class = "login.css" })
#Html.ValidationMessageFor(model => model.loginModel.username);
#Html.Password("Password", null, new { placeholder = "Password",
#class = "login.css" })
#Html.ValidationMessageFor(model => model.loginModel.password);
<div class="form-submit">
<button type="submit" value="Submit" class="submit"
id="submit" name="submit">Login</button>
</div>
<p class="message">Not registered? Create an account</p>
</form>
}
</div>
</div>
<img class="coffee-image" src="~/images/coffee.gif" alt="">
It is giving the following error
System.NullReferenceException: 'Object reference not set to an instance
of
an object.'
ReadingCat.Models.DatabaseCombinedWithOtherModel.loginModel.get returned
null.
I think this error about not fully declare DatabaseCombinedWithOtherModelthis model.
Please try this code.
private int userid
DatabaseCombinedWithOtherModel modelobj = new DatabaseCombinedWithOtherModel();
// GET: Login
[HttpGet]
public ActionResult Login()
{
return View(modelobj);
}
And also try this thing that before call the model in view firstly add some
values in objects from controller side pass return View(modelobj);and then call in view side.
Since you are not seting the loginmodel, it will be null Which will throw the exception.
Either initialize the loginModel in the otherModel's constructor or in the Login get action.
Try
namespace ReadingCat.Models
{
public class DatabaseCombinedWithOtherModel
{
public DatabaseModel databaseModel { get; set; }
public LoginModel loginModel { get; set; }
}
public DatabaseCombinedWithOtherModel()
{
loginModel = new LoginModel();
databaseModel = new DatabaseModel();
}
}
or
[HttpGet]
public ActionResult Login()
{
var vm = new DatabaseCombinedWithOtherModel()
vm.loginModel = new LoginModel();
vm.databaseModel = new DatabaseModel();
return View(vm);
}
The mentioned error goes away if #Html.Textbox is replaced with #Html.EditorFor. Then, another error arises with the DatabaseModel. It returns null. So I created a different object of DatabaseModel and worked with that.
I am working on a scheduling project in MVC 5. I want to check the Date Selected against current Date. If they match, display the scheduled appointment for Today' date only in a view. Currently, the appointments do not display when I add "appointment.AppointmentDate == DateTime.Now". Instead the View duplicates "No Appointments Today'.
I have researched through StackOverFlow and other sites to try and figure out with no luck. One attempt was adding ".data('date') in the Create view: " $('#datetimepicker1.data('date'), #datetimepicker2').datetimepicker " to set the type as Date but was unsuccessful. I am a beginner and hope someone may be able to help me in the right direction. Thanks.
My code is below:
MODEL:
public enum AppointmentTime
{
_1pm_to_2pm, ApplicationDbContext _dbContext = new ApplicationDbContext();
public ActionResult Create(int id)
{
Property property = _dbContext.Properties.SingleOrDefault(b => b.PropertyID == id);
ViewBag.propertyName = property.PropertyName;
Consultation consultation = new Consultation();
consultation.PropertyID = id;
return View(consultation);
}
[HttpPost]
public ActionResult Create(Consultation consultation)
{
try
{
if (ModelState.IsValid)
{
Property property = _dbContext.Properties.SingleOrDefault(b => b.PropertyID == consultation.PropertyID);
property.Consultations.Add(consultation);
_dbContext.SaveChanges();
return RedirectToAction("Details", "Property", new { id = property.PropertyID });
}
return View();
}
catch
{
return View("Error");
}
}
_2pm_to_3pm,
_3pm_to_4pm,
_4pm_to_5pm,
_5pm_to_6pm
}
public class Consultation
{
[Key]
public int AppointmentID { get; set; }
[ForeignKey("Property")]
public int PropertyID { get; set; }
[Display(Name = "Enter your name")]
public string AppointmentName { get; set; }
[Required]
[Display(Name = "Email")]
public string AppointmentEmail { get; set; }
[Display(Name = "Select Date")]
[UIHint("AppointmentDate")]
[Required]
public DateTime AppointmentDate { get; set; }
public AppointmentTime AppointmentTime { get; set; }
public virtual Property Property { get; set; }
}
CONTROLLOR:
[Authorize(Roles = "Admin")]
public ActionResult AdminAppointmentView(Consultation consultaion)
{
var appointments = _dbContext.Consultations.ToList();
return View(appointments);
}
CREATE VIEW
#model OpenProperties.Models.Consultation
#{
ViewBag.Title = "Create Appointment";
}
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css"
rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen"
href="http://tarruda.github.com/bootstrap-datetimepicker/assets/css/bootstrap-datetimepicker.min.css">
#using (Html.BeginForm(new { enctype = "multipart/form-data", id = "form1" }))
{
#Html.ValidationSummary(true)
<div>
#Html.HiddenFor(model => model.PropertyID)
<br />
<div class="form-group">
#Html.LabelFor(m => m.AppointmentName)
#Html.TextBoxFor(m => m.AppointmentName, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.AppointmentEmail)
#Html.TextBoxFor(m => m.AppointmentEmail, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.AppointmentTime, new { #class = "control-label col-md-2" })
#Html.EnumDropDownListFor(model => model.AppointmentTime)
#Html.ValidationMessageFor(model => model.AppointmentTime)
</div>
<div id="datetimepicker1" class="input-append date">
#Html.TextBoxFor(m => m.AppointmentDate, "{0:dd/MM/yyyy HH}",
new { placeholder = "App Date", #class = "dtPicket" })
<span class="add-on">
<i data-time-icon="icon-time"
data-date-icon="icon-calendar"></i>
</span>
</div>
<br />
<br />
<input type="submit" value="Submit" />
</div>
<script type="text/javascript"
src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js">
</script>
<script type="text/javascript"
src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/js/bootstrap.min.js">
</script>
<script type="text/javascript"
src="http://tarruda.github.com/bootstrap-datetimepicker/assets/js/bootstrap-datetimepicker.min.js">
</script>
<script type="text/javascript"
src="http://tarruda.github.com/bootstrap-datetimepicker/assets/js/bootstrap-datetimepicker.pt-BR.js">
</script>
<script type="text/javascript">
$('#datetimepicker1, #datetimepicker2').datetimepicker({
format: 'dd/MM/yyyy'
});
</script>
}
AdminAppointmentView VIEW:
#model IEnumerable<OpenProperties.Models.Consultation>
<h2>Todays Appointments</h2>
#foreach (var appointment in Model)
{
if (appointment.AppointmentDate == DateTime.Now)
{
<li>
<ul>#appointment.AppointmentID</ul>
<ul>#appointment.AppointmentDate</ul>
<ul>#appointment.AppointmentEmail</ul>
<ul>#appointment.AppointmentName</ul>
<ul>For Property ID: #appointment.PropertyID</ul>
</li>
}
else
{
<ul> No Appointments Today </ul>
}
}
Just at first Glance.
if (appointment.AppointmentDate == DateTime.Now)
(
}
Is the date Format is same for appointment.AppointmentDate and DateTime.Now??.
If not just add Tostring Method to these date.
ex.
DateTime.Now.ToString("MMMM dd, yyyy")
2) You might check the // Difference in days, hours, and minutes. between the date as well. Instead of comparing
DateTime oldDate = new DateTime(2002,7,15);
DateTime newDate = DateTime.Now;
// Difference in days, hours, and minutes.
TimeSpan ts = newDate - oldDate;
// Difference in days.
int differenceInDays = ts.Days;
3) You can format date in datetime Picker as follows.
$('#datepicker').datetimepicker({
dateFormat: "yy-mm-dd",
timeFormat: "hh:mm:ss"
});
4) Also could you please put Script and datepicker code before the html
<script type="text/javascript"
src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js">
</script>
<script type="text/javascript"
src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/js/bootstrap.min.js">
</script>
<script type="text/javascript"
src="http://tarruda.github.com/bootstrap-datetimepicker/assets/js/bootstrap-datetimepicker.min.js">
</script>
<script type="text/javascript"
src="http://tarruda.github.com/bootstrap-datetimepicker/assets/js/bootstrap-datetimepicker.pt-BR.js">
</script>
<script type="text/javascript">
$('#datetimepicker1, #datetimepicker2').datetimepicker({
format: 'dd/MM/yyyy'
});
</script>
I have a MVC3 application that populates a dropdownlist from a Model. When I select an item from the list , i would like to Update the url ('/Edit/4') on a single 'edit' link which will allow me display the edit view, i.e rather than use a template which creates edit links for all records in the model, I would like to use one edit link and then update it as items are selected in the dropdownlist. I have been able to achieve some of this using jquery , I would like to do it in C# code using MVC.
Any thoughts??
I created a Sample code. I have an area. Highlighted part is your concerned code.
Controller
public class DropdownController : Controller
{
[HttpGet]
public ActionResult DropDownListFor()
{
Practise.Areas.MyPractise.Models.Dropdown d = new Models.Dropdown();
return View(d);
}
public ActionResult Edit(string Val)
{
return View();
}
}
Model
public class Dropdown
{
[Required(ErrorMessage = "Please select state")]
public string StateId { get; set; }
public int MyProperty { get; set; }
public List<SelectListItem> States
{
get
{
return new List<SelectListItem>()
{
new SelectListItem
{
Text = "Punjab",
Value = "Pb",
Selected = false
},
new SelectListItem
{
Selected = false,
Value = "HM",
Text = "Himachal"
}
};
}
}
}
DropDownListFor View
#model Practise.Areas.MyPractise.Models.Dropdown
<!DOCTYPE html>
<html>
<head>
<title>DropDownListFor</title>
<script src="/Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#StateId').change(function () {
if($('#StateId option:selected').val() == "")
$('.edit').attr('href', "#");
else
$('.edit').attr('href',
"#Url.Action("Edit", "Dropdown",
new RouteValueDictionary(new { area = "MyPractise" }))"
+ "/" + $('#StateId option:selected').text());
});
});
</script>
</head>
<body>
#using (Html.BeginForm("DropDownListFor", "DropDown", FormMethod.Post,
new { id = "DropDownList" }))
{
#Html.DropDownListFor(m => m.StateId, Model.States, "select");
#Html.ValidationMessageFor(m => m.StateId);
<a class="edit" href="#">Edit</a>
<input type="submit" name="Submit" value="Submit" />
}
</body>
</html>
Area registration under MyPractiseAreaRegistration class under highlighted area
public class MyPractiseAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "MyPractise";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"MyPractise_default1",
"MyPractise/{controller}/{action}/{Val}",
new { action = "Index", Val = UrlParameter.Optional }
);
context.MapRoute(
"MyPractise_default",
"MyPractise/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
I have View:
#model MvcApplication2.Models.HomeModel
#{
ViewBag.Title = "Home";
}
<h2>Home</h2>
<a>Witaj</a>
#Model.UserName
#using (Html.BeginForm())
{
<a>Podaj hasło</a>
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(x => x.Password)
<input type="submit" />
}
and Controller
using System.Web.Mvc;
using System.Web.UI;
using MvcApplication2.Models;
namespace MvcApplication2.Controllers
{
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public class HomeController : Controller
{
public ActionResult Index()
{
HomeModel model = new HomeModel() { UserName = "John" };
return View(model);
}
public JsonResult CheckPassword(string password)
{
bool result = false;
if (password.Length < 4)
{
result = false;
}
else
{
result = true;
}
return Json(result, JsonRequestBehavior.AllowGet);
}
}
}
and Model:
using System.Web.Mvc;
namespace MvcApplication2.Models
{
public class HomeModel
{
public string UserName { get; set; }
[Remote("CheckPassword", "Home", ErrorMessage = "Wpisz lepsze hasło")]
public string Password { get; set; }
}
}
When should remote validation method fire? When I click input button? What I do wrong?
Are you missing unobtrusive jQuery references in your view / Razor Layout?
<script src="#Url.Content("~/Scripts/jquery-1.7.1.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
In CheckPassword method password variable is not matching with the Model Password. It should be in TitleCase :-
CheckPassword(string Password)
I'm following [Getting started with ASP.NET MVC 3][1]. And I can't add/edit with value of Price = 9.99 or 9,99. It said: "The value '9.99' is not valid for Price." and "The field Price must be a number."
How to fix this?
Model:
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
public class MovieDbContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
}
Controller:
public class MovieController : Controller
{
private MovieDbContext db = new MovieDbContext();
//
// GET: /Movie/
public ViewResult Index()
{
var movie = from m in db.Movies
where m.ReleaseDate > new DateTime(1984, 6, 1)
select m;
return View(movie.ToList());
}
//
// GET: /Movie/Details/5
public ViewResult Details(int id)
{
Movie movie = db.Movies.Find(id);
return View(movie);
}
//
// GET: /Movie/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Movie/Create
[HttpPost]
public ActionResult Create(Movie movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(movie);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
//
// GET: /Movie/Edit/5
public ActionResult Edit(int id)
{
Movie movie = db.Movies.Find(id);
return View(movie);
}
//
// POST: /Movie/Edit/5
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
//
// GET: /Movie/Delete/5
public ActionResult Delete(int id)
{
Movie movie = db.Movies.Find(id);
return View(movie);
}
//
// POST: /Movie/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
Movie movie = db.Movies.Find(id);
db.Movies.Remove(movie);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
View:
#model MvcMovies.Models.Movie
#{
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>Movie</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ReleaseDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ReleaseDate)
#Html.ValidationMessageFor(model => model.ReleaseDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Genre)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Genre)
#Html.ValidationMessageFor(model => model.Genre)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Price)
#Html.ValidationMessageFor(model => model.Price)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
public DbSet<Movie> Movies { get; set; }
}
I just stumbled on this again after 2 years. I thought ASP.NET MVC 5 had solved this but looks like it's not the case. So here goes how to solve the problem...
Create a class called DecimalModelBinder like the following and add it to the root of your project for example:
using System;
using System.Globalization;
using System.Web.Mvc;
namespace YourNamespace
{
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;
if(valueResult.AttemptedValue != string.Empty)
{
try
{
actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
}
catch(FormatException e)
{
modelState.Errors.Add(e);
}
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
}
Inside Global.asax.cs, make use of it in Application_Start() like this:
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
You are one of the non-English customers, which MS has not foreseen. You will need to put some extra effort into making your version run. I had a similar problem, denying me both "9,99" and "9.99" as valid numbers. It seems like once server-side validation failed, and once client-side validation, causing no number to be accepted.
So you have to make the validation congruent.
Like suggested in the comments, have a look at
http://msdn.microsoft.com/en-us/library/gg674880(VS.98).aspx
and
http://haacked.com/archive/2010/05/10/globalizing-mvc-validation.aspx
and
MVC 3 jQuery Validation/globalizing of number/decimal field
or - should you understand German (or just look at the code examples)
http://www.andreas-reiff.de/2012/06/probleme-mit-mvcmovies-beispiel-validierung-des-preises-mit-dezimalstellen-schlagt-fehl/
BTW, same problem exists for both the Music and Movie example tutorials.
I encountered this issue when developing a web application for an English audience, on a Pc in The Netherlands.
A model property of type double, generated this server-side validation error:
The value '1.5' is not valid for .
On an breakpoint, I saw these values in the Immediate Window:
?System.Threading.Thread.CurrentThread.CurrentUICulture
{en-US}
?System.Threading.Thread.CurrentThread.CurrentCulture
{nl-NL}
As a solution (or maybe a work-around), you can specify the globalization settings in the web.config file.
<configuration>
<system.web>
<globalization culture="en" uiCulture="en" />
Of course this means that you force your users to enter numbers in English formatting, but that is just fine, in my case.
In 2019, this problem is still not solved. Using ASP Core 2.1, my UI is in French (decimal separator= ',') and I couldn't get the validation to work anytime I had a decimal number.
I found a workaround, not ideal though: I created a french-based CultureInfo but I changed the decimal separator to be the same as in Invariant Culture : '.'.
This made the trick, my decimal numbers are now displayed US style (but I am ok with it) and validation works.
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
//Culture specific problems
var cultureInfo = new CultureInfo("fr-FR");
cultureInfo.NumberFormat.NumberDecimalSeparator = ".";
System.Threading.Thread.CurrentThread.CurrentUICulture = cultureInfo;
}
I've adapted the code from Leniel Macaferi a little bit so you can use it for any type:
public class RequestModelBinder<TBinding> : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult valueResult = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName);
ModelState modelState = new ModelState { Value = valueResult };
object actualValue = null;
if (valueResult.AttemptedValue != string.Empty)
{
try
{
// values really should be invariant
actualValue = Convert.ChangeType(valueResult.AttemptedValue, typeof(TBinding), CultureInfo.CurrentCulture);
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
I've tried #Leniel Macaferi but it didn't work for me.
ModelState.IsValid didn't accept numbers formatted like 7.000,00
The problem started when I changed the property type from:
[Column("PRICE")]
public decimal Price { get; set; }
to
[Column("PRICE")]
public decimal? Price { get; set; }
I've also tried to include the globalization on web.config that I had forgotten
<globalization culture="pt-BR" uiCulture="pt-BR" enableClientBasedCulture="true" />
The only workaround that worked was change back the property to decimal only:
[Column("PRICE")]
public decimal Price { get; set; }
and also changed the table column to NOT accept null values
Hope it helps somebody.
You can add:
protected void Application_BeginRequest()
{
var currentCulture = (CultureInfo)CultureInfo.CurrentCulture.Clone();
currentCulture.NumberFormat.NumberDecimalSeparator = ".";
currentCulture.NumberFormat.NumberGroupSeparator = " ";
currentCulture.NumberFormat.CurrencyDecimalSeparator = ".";
Thread.CurrentThread.CurrentCulture = currentCulture;
//Thread.CurrentThread.CurrentUICulture = currentCulture;
}
To Global.asax (tested on MVC 5.1). It works without changing UICulture for me.
I solved this problem by disabled jquery for price and only validate on server side for that input. I found the answer here:
ASP .NET MVC Disable Client Side Validation at Per-Field Level
<div class="col-md-10">
#{ Html.EnableClientValidation(false); }
#Html.EditorFor(model => model.DecimalValue, new { htmlAttributes = new { #class = "form-control" } })
#{ Html.EnableClientValidation(true); }
#Html.ValidationMessageFor(model => model.DecimalValue, "", new { #class = "text-danger" })
</div>
This is an extension of Leniel Maccaferri solution that avoids problems related to the sending by the user-agent of decimal values in a different culture format from the server one. Its limits are bound to the thousands separator parsing that, when it is the only separator in the value, can raise wrong bindings.
/// <summary>
/// custom decimal model binder
/// </summary>
/// <author>https://stackoverflow.com/users/114029/leniel-maccaferri</author>
/// <see cref="https://stackoverflow.com/a/19339424/3762855"/>
/// <remarks>Integrated with a fix for the decimal separator issue.
/// <para>This issue maybe depends from browsers interpretation of decimal values posted-back to the server when they receive response without any http content-language specific indication.</para>
/// <para>Important! decimal values caming from UI must not be formatted with thousands separator.</para>
/// </remarks>
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;
if (valueResult.AttemptedValue != string.Empty)
{
try
{
var culture = Thread.CurrentThread.CurrentCulture;
//This is needed to convert in the right manner decimal values coming from UI, as seems they always represent the decimal separator as a period(".").
//Maybe depends from browsers interpretation of decimal values posted-back to the server when they receive response without any http content-language specific indication.
if (culture.NumberFormat.NumberDecimalSeparator == "," && valueResult.AttemptedValue.LastIndexOf(".") > 0)
{
culture = new CultureInfo("en");
}
else if (Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator == "." && valueResult.AttemptedValue.LastIndexOf(",") > 0)
{
culture = new CultureInfo("it");
}
actualValue = Convert.ToDecimal(valueResult.AttemptedValue, culture);
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
Just comment this link for the script:
<%--<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>--%>