Model.IsValid is always false when I insert a datetime - asp.net-mvc-3

I have the following class:
public class Post
{
[DataType(DataType.Date, ErrorMessage="Please fill in a valid date.")]
[RegularExpression(#"^\d{1,2}\/\d{1,2}\/\d{4}$", ErrorMessage="Fill in a valid date.")]
public DateTime? PublishDate { get; set; }
}
and in my Edit action I have it like this
[HttpPost]
public ActionResult Edit(Post post)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
else
{
return View(post);
}
}
But my ModelState is always invalid. How come? How can I solve this?
I also posted here: http://forums.asp.net/t/1663783.aspx/1?MVC3+how+to+check+datetime+on+model+with+unobtrusive+javascript+.
Update: I found that there is indeed an error via:
ModelState.Values.Select(x => x.Errors);
But how can I find out how it gets there? And more important, how can I solve it?

Your regular expression does not work with the date that you enter. I would simply remove it since the DateTime struct will not be assigned if the date can't be parsed by the model binder.

Related

TextBoxFor on a boolean field renders the same value even if it was modified on controller side

I have a simple form with a textbox (and a model editor I want to render in specific cases)
#using (Html.BeginForm("Import", "Flow"))
{
#Html.TextBoxFor(model => model.IsConfirmed)
#if (Model.IsConfirmed)
{
#Html.EditorFor(m => m.Preview)
}
}
The model used in this view is the following
public class ImportViewModel
{
public Boolean IsConfirmed { get; set; }
public PreviewViewModel Preview { get; set; }
public ImportViewModel()
{
this.IsConfirmed = false;
}
}
The form posts on the following controller
public class FlowController
{
[HttpPost]
public ActionResult Import(ImportViewModel model)
{
try
{
if (ModelState.IsValid)
{
if (model.IsConfirmed)
{
// do something else
}
else
{
model.Preview = Preview(model.strCA, model.SelectedAccount);
model.IsConfirmed = true;
return View(model);
}
}
}
catch (Exception ex)
{
throw new Exception("arf", ex);
}
return RedirectToAction("Index", "Home");
}
}
On first load, the textbox contains "false"
When posted, the property IsConfirmed of the model is set to "true" and this model is passed to the same view.
I expect the textbox to be "true" but it is still "false"... moreover the Preview property is correctly rendered, so it means Model.IsConfirmed is indeed true...
Am I missing something ?
Thanks
Make sure you remove the value from the ModelState if you intend to modify it:
ModelState.Remove("IsConfirmed");
model.IsConfirmed = true;
The reason you need to do that is because, by design, all Html helpers (such as TextBoxFor) will first look for a value in the ModelState when binding and only not found they will use the value on your model. And since there's a value with the same name in the ModelState (coming from the POST request), that's what's being used.

Custom error message not working for DateTime validation in asp net mvc 3

I have a ViewModel with a String property and the following Data Annotation :
Edit to work with string
[DataType(DataType.Date, ErrorMessage="Not Working !!!")]
public String StringBirthDate1 { get; set; }
That's my view
#Html.EditorFor(model => model.StringBirthDate1 )
#Html.ValidationMessageFor(model => model.StringBirthDate1)
If I run my application and put an invalid Date like '---' or 29.02.1900 I don't get any validation error !
Ok I've given up trying to use built-in MVC tools for data validation !
I did a custom Validation Attribute :
public class ValidDateStringAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
DateTime dtout;
if (DateTime.TryParse(value.ToString(), out dtout ))
{
return true;
}
return false;
}
}
Here is my View Model decorated with the custom attribute :
[ValidDateString(ErrorMessage="Invalid date format")]
public String BirthDate1 { get; set; }
Works like a charm :-)
It seems to me, that [DataType(DataType.Date, ErrorMessage="Not Working !!!")] working when it attached to string property. Try to use:
[DataType(DataType.Date, ErrorMessage="Not Working !!!")]
puplic string StringBirthDate1{get;set;}
public DateTime BirthDate1
{
get{return DateTime.Parse(StringBirthDate1);}
set{StringBirthDate1 = value.ToString();}
}
I didn't like any of the solutions I found so I kept poking at possibilities until I came up with one I do like. I added a regular expression validator utilizing the regular expression from this article: http://answers.oreilly.com/topic/226-how-to-validate-traditional-date-formats-with-regular-expressions/
[Required(ErrorMessage = "Birthdate is required. [MM/DD/YYYY]")]
[RegularExpression(#"^([1-9]|0[1-9]|1[0-2])[- / .]([1-9]|0[1-9]|1[0-9]|2[0-9]|3[0-1])[- / .](1[9][0-9][0-9]|2[0][0-9][0-9])$", ErrorMessage = "Birthdate must be in MM/DD/YYYY format.")]
public Nullable<DateTime> Birthdate { get; set; }
The result is, if the field is blank I get the required error message and if anything is in the field, but it is not a valid date, I get the regular expression message.
I might add that it seems very silly that [DataType] doesn't accept an error message. I tried exactly like the original author of this thread. That would have been logical and intuitive.

ASP.NET MVC 2: Model Validation - username already taken?

I am following Scott Gu's blog: here
In his Blog he talks about client and server side validation.
How does one validate if username has already been taken and display this as a validation error message to the user?
In Scott's blog, this would be the same as validating if Title is unique:
public class Dinner
{
public int DinnerID { get; set; }
[Required(ErrorMessage = "Please enter a Dinner Title")]
[StringLength(20, ErrorMessage = "Title is too long")]
public string Title { get; set; }
[Required(ErrorMessage = "Please enter the Date of the Dinner")]
public DateTime EventDate { get; set; }
[Required(ErrorMessage = "Please enter the location of the Dinner")]
[StringLength(30, ErrorMessage = "Address is too long")]
public string Address { get; set; }
[Required(ErrorMessage = "Please enter your email address")]
[RegularExpression(".+\\#.+\\..+", ErrorMessage = "Please enter a valid email address")]
public string HostedBy { get; set; }
public virtual ICollection<RSVP> RSVPs { get; set; }
}
My first guess is that somehow this is done within the Model Controller, here:
//
// POST: /Home/Create
[HttpPost]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
nerdDinners.Dinners.Add(dinner);
nerdDinners.SaveChanges();
return RedirectToAction("Index");
}
return View(dinner);
}
And because the Title is stored in a database server, this would be server side validation.
I know how to check if the Title is unique, but I do not know how to make the validation message appear in the View like it does using declaratives like [Required] or [StringLength()]. For example, here is how I can check for uniqueness:
[HttpPost]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
foreach (var existingDinner in nerdDinners.Dinners)
{
if(existingDinner.Title == dinner.Title)
{
**// TODO: display validation error message?**
}
}
nerdDinners.Dinners.Add(dinner);
nerdDinners.SaveChanges();
return RedirectToAction("Index");
}
return View(dinner);
}
Using my imagination, and a magic wand, I would want to create a new declarative called [TitleIsUnique] that performs like the other validation declaratives.
Thank you in advance for your assistance.
You could create a custom attribute as mentioned and use IValidateObject but I prefer to add my errors to the ModelState in one of the layers in my application.
For this you can use ModelState.AddModelError
If you use ModelState.AddModelError("Title", "Title must be unique"); it will add an error to the Title field.
If you use ModelState.AddModelError("*", "Title must be unique"); it will add a general error message for the page.
[HttpPost]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
if(nerdDinners.Dinners.Any(d => d.Title == dinner.Title))
{
ModelState.AddModelError("Title", "The title is not unique");
return View(dinner);
}
nerdDinners.Dinners.Add(dinner);
nerdDinners.SaveChanges();
return RedirectToAction("Index");
}
return View(dinner);
}
You are probably looking at implementing your own attribute derived from CustomAttribute. Take a look at this blog post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx - it shows how to validate uniqueness. In the post IValidatableObject interface is used to perform validation but you should be able to the same by creating CustomAttribute.

MVC3 - Validating inputs - Difference between create() and edit()

I'm again struggling at validating inputs.
Let's say I edit a customer and the field "name" is required via
[Required(ErrorMessage = Constants.ErrorMsgNameMissing)]
public string NAME { get; set; }
inside the model.
The edit method does
[HttpPost]
edit(ViewModel vm)
{
// some code here
try
{
UpdateModel(vm);
// some code there
}
catch (Exception e)
{
return View(vm);
}
}
While doing UpdateModel(vm), an exception is thrown if the name is empty. Then my view shows the Html.ValidationSummary(). So far, so good.
Now, if I create a customer via
[HttpPost]
create(ViewModel vm)
{
if (ModelState.IsValid) { ... }
}
I don't have the method UpdateModel() since there's nothing to update. And ModelState.IsValid seems to return true every time. Even if the ViewModel is null. So I run into trouble then.
How do I validate this? And what do I return in case of errors?
Update: I think it was too late yesterday. In fact, it DOES work. But I was hoping for an exception and forgot the else { ... }...
Try this:
[HttpPost, ValidateInput(true)]
create(ViewModel vm)
{
if (ModelState.IsValid) { ... }
}

Argument is always null when using RemoteAttribute

I am trying to make my first remote-validation in MVC, which I cannot get to work since the argument my validation action receives is always null.
public class Book
{
[Remote("IsValidDate", "Validation")]
public DateTime ReleaseDate { get; set; }
}
Then I have some other values I need outside the Book class, wherefore I also created a BookModel:
public class BookModel
{
public Book Book;
public string SomeOtherValueNotInterestingInThisExample;
public BookModel(Book book)
{
Book = book;
// other stuff
}
}
Then my editing page contains:
...
<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>
.... and later
<div class="editor-label">
#Html.LabelFor(model => model.Book.ReleaseDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Book.ReleaseDate)
#Html.ValidationMessageFor(model => model.Book.ReleaseDate)
</div>
My ValidationController looks like this:
public class ValidationController : Controller
{
public JsonResult IsValidDate(string strDate)
{
bool isValid = DateHelper.IsValid(strDate);
if (isValid)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
else
{
return Json("(remote - not valid)", JsonRequestBehavior.AllowGet);
}
}
}
My problem is, that my strDate argument in IsValidDate(string strDate) is always null. Appart from that my validation works fine, and if I force it to fail, it also returns the correct errormessage to the correct field.
I cannot figure out why strDate is always null. Is it because it is in a BookModel and thereby called "Book.ReleaseDate"?
Okay, here is a hack that works for me in this case. First I must admit that I was not aware that the argument in IsValidDate must be named the same as the field I want to validate. I know that now.
Here is the changed code:
public class ValidationController : Controller
{
public JsonResult IsValidDate(string date)
{
if (date == null)
date = GetQueryStringValue("date");
bool isValid = DateHelper.IsValid(date);
if (isValid)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
else
{
return Json(false, JsonRequestBehavior.AllowGet);
}
}
private string GetQueryStringValue(string key)
{
return (from qStr in Request.QueryString.AllKeys
where qStr.ToLower().EndsWith(key.ToLower())
select Request.QueryString.Get(qStr)).FirstOrDefault();
}
}
Note that I have made a separate GetQueryStringValue, so other validators can use the same method.
My Book class now looks like this (only the problematic fields are mentioned here):
public class Book
{
[Remote("IsValidDate", "Validation", ErrorMessage = "Release date is not valid")]
public DateTime ReleaseDate { get; set; }
[Remote("IsValidDate", "Validation", ErrorMessage = "Start date is not valid")]
public DateTime StartDate { get; set; }
}
Comment to the solution: Basically I start to check if the argument in IsValidDate is null or not. If it is null, I go through all QueryString keys to see if there is an argument which ends with the field name I want and then populate my argument with that value.
That led me to another solution (which ONLY works here, because I know that I do not have additional fields that ends with the same field name - or should I say - partly field name). By saying that I only want to look at the partly key name "date" (instead of "releasedate") I can have this validator check all my date-fields in the View. But as mentioned: it only works, because I do not want additional fields in my Remote validation. If I wanted to compare e.g. "StartDate" with "ReleaseDate" I would get problems :-)
I have also moved my ErrorMessage to my DataAnnotations, so I can have different errormessages on the fields.
It is not nice - but it works!

Resources