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
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.
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.
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.
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) { ... }
}
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!