simple custom validation,
my model and custom validation:
public class Registration
{
[Required(ErrorMessage = "Date of Birth is required")]
[AgeV(18,ErrorMessage="You are not old enough to register")]
public DateTime DateOfBirth { set; get; }
}
public class AgeVAttribute : ValidationAttribute
{
private int _maxAge;
public AgeVAttribute(int maxAge)
{
_maxAge = maxAge;
}
public override bool IsValid(object value)
{
return false; <--- **this never gets executed.... what am I missing?**
}
}
(Please see the inline comment above)
view:
#using (Html.BeginForm()) {
#Html.ValidationSummary("Errors")
<fieldset>
<legend>Registration</legend>
<div class="editor-label">
#Html.LabelFor(model => model.DateOfBirth)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateOfBirth)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Can't repro.
Model:
public class Registration
{
[Required(ErrorMessage = "Date of Birth is required")]
[AgeV(18, ErrorMessage = "You are not old enough to register")]
public DateTime DateOfBirth { set; get; }
}
public class AgeVAttribute : ValidationAttribute
{
private int _maxAge;
public AgeVAttribute(int maxAge)
{
_maxAge = maxAge;
}
public override bool IsValid(object value)
{
return false;
}
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new Registration
{
DateOfBirth = DateTime.Now.AddYears(-10)
});
}
[HttpPost]
public ActionResult Index(Registration model)
{
return View(model);
}
}
View:
#model Registration
#using (Html.BeginForm())
{
#Html.ValidationSummary("Errors")
<fieldset>
<legend>Registration</legend>
<div class="editor-label">
#Html.LabelFor(model => model.DateOfBirth)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateOfBirth)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
The IsValid method is always hit when the form is submitted. Also notice that I haven't enabled client side validation because I didn't include the jquery.validate.js and the jquery.validate.unobtrusive.js scripts. If you have included them and there's an error chances are that client side validation will prevent your form from even being submitted to the server in which case it would be normal for the IsValid method not being invoked.
Related
I use Jquery Ajax Form Plugin to upload file. Codes:
AuthorViewModel
public class AuthorViewModel
{
public int Id { get; set; }
[Required(ErrorMessage = "{0} alanı boş bırakılmamalıdır!")]
[Display(Name = "Yazar Adı")]
public string Name { get; set; }
[Display(Name = "Kısa Özgeçmiş")]
public string Description { get; set; }
[Display(Name = "E-Posta")]
public string Email { get; set; }
public string OrginalImageUrl { get; set; }
public string SmallImageUrl { get; set; }
}
Form
#using (Html.BeginForm("_AddAuthor", "Authors", FormMethod.Post, new { id = "form_author", enctype = "multipart/form-data" }))
{
<div class="editor-label">
<input type="file" name="file" id="file" />
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
</div>
<div class="editor-field">
#Html.ValidationMessageFor(model => model.Name)
</div>
...
<div class="submit-field">
<input type="submit" value="Ekle" class="button_gray" />
</div>
}
Script
<script>
$(function () {
$('#form_author').ajaxForm({
beforeSubmit: ShowRequest,
success: SubmitSuccesful,
error: AjaxError
});
});
function ShowRequest(formData, jqForm, options) {
$(".loading_container img").show();
}
function AjaxError() {
alert("An AJAX error occured.");
}
function SubmitSuccesful(result, statusText) {
// Veritabanı işlemleri başarılı ise Index sayfasına
// geri dön, değilse partial-view sayfasını yenile
if (result.url) {
window.location.href = result.url;
} else {
$(".authors_content_container").html(result);
}
}
</script>
Controller
[HttpPost]
public ActionResult _AddAuthor(AuthorViewModel viewModel, HttpPostedFileBase file)
{
...
viewModel.OrginalImageUrl = file.FileName;
...
}
Above codes work fine
Question
As you see, I post file seperate from ViewModel. Is there a way to add HttpPostedFileBase file property to ViewModel and bind it to viewModel in view, And post it to controller in ViewModel?
I hope , I can explain.
EDIT:
This codes work fine. I dont want post , viewModel and HttpPostedFile seperately. I want something like this: (If it is possible.)
Model
public class AuthorViewModel
{
public int Id { get; set; }
[Required(ErrorMessage = "{0} alanı boş bırakılmamalıdır!")]
[Display(Name = "Yazar Adı")]
HttpPostedFileBase file{ get; set; }
...
}
Controller
[HttpPost]
public ActionResult _AddAuthor(AuthorViewModel viewModel)
{
var file = viewModel.file;
...
}
Thanks.
Yes you can add AliRıza Adıyahşi.
Here is the property to do it:
public HttpPostedFileBase File { get; set; }
Now in you form you should add enctype as Xiaochuan Ma said:
#using (Html.BeginForm("_AddAuthor", "Authors", FormMethod.Post, new { id = "form_author", enctype="multipart/form-data" }))
{
<div class="editor-label">
<input type="file" name="file" id="file" />
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
</div>
<div class="editor-field">
#Html.ValidationMessageFor(model => model.Name)
</div>
...
<div class="submit-field">
<input type="submit" value="Ekle" class="button_gray" />
</div>
}
On you Controller action:
[HttpPost]
public ActionResult _AddAuthor(AuthorViewModel viewModel, HttpPostedFileBase file)
{
if(file!=null)
{
viewModel.File=file; //Binding your file to viewModel property
}
//Now you can check for model state is valid or not.
if(ModelState.IsValid)
{
//do something
}
else
{
return View(viewModel);
}
}
Hope it helps. Is this what you need ?
EDIT
There is nothing additional to do. Its automatically binding to viewModel.
[HttpPost]
public ActionResult _AddAuthor(AuthorViewModel viewModel)
{
var uploadedfile = viewModel.File;// Here you can get the uploaded file.
//Now you can check for model state is valid or not.
if(ModelState.IsValid)
{
//do something
}
else
{
return View(viewModel);
}
}
This worked for me !
Yes, you can add HttpPostedFileBase in to your ViewModel, and add enctype = "multipart/form-data" to your From in HTML.
Check with this link:
MVC. HttpPostedFileBase is always null
Given model:
public class RegisterModel
{
<!-- some properties -->
[Required]
public Dictionary<DayOfWeek, bool> DaysAtWork { get; set; }
public RegisterModel()
{
DaysAtWork = new Dictionary<DayOfWeek, bool>
{
{DayOfWeek.Monday, true},
{DayOfWeek.Tuesday, true},
{DayOfWeek.Wednesday, true},
{DayOfWeek.Thursday, true},
{DayOfWeek.Friday, true}
};
}
}
Controller:
public ActionResult Register()
{
var model = new RegisterModel();
return View(model);
}
[HttpPost]
public ActionResult Register(RegisterModel model)
{
<!-- and at these point in model state property for DaysAtWork
Dictionary is null and the whole model is invalid - and property in model
parameter is also null -->
if (ModelState.IsValid)
{
<!-- some logic here -->
}
return View(model);
}
And View:
<!-- some code -->
#using (Html.BeginForm()) {
#Html.ValidationSummary(true, "Account creation was unsuccessful. Please correct the errors and try again.")
<div>
<fieldset>
<legend>Account Information</legend>
<div>Days at work</div>
<div>
Monday #Html.CheckBoxFor(m => m.DaysAtWork[DayOfWeek.Monday])
</div>
<div>
Tuesday #Html.CheckBoxFor(m => m.DaysAtWork[DayOfWeek.Tuesday])
</div>
<div>
Wednesday #Html.CheckBoxFor(m => m.DaysAtWork[DayOfWeek.Wednesday])
</div>
<div>
Thursday #Html.CheckBoxFor(m => m.DaysAtWork[DayOfWeek.Thursday])
</div>
<div>
Friday #Html.CheckBoxFor(m => m.DaysAtWork[DayOfWeek.Friday])
</div>
<p>
<input type="submit" value="Register" />
</p>
</fieldset>
</div>
}
I've a problem while trying to register user, because in model DaysAtWork property is set to null. How can I tell asp to use that model which was created in Register() HttpGet method?
What do I do wrong?
If you make a Model called 'Week' or something similar as such:
public Week(){
public int Id;
public int AccountId;
public bool Monday;
public bool ...;
public bool Friday;
}
and add a reference to this in your Registermodel.
Hope this helps!
I can't seem to figure out how to validate the pieces of a partial view for an ViewModel that has the partial ViewModel as a child object. Here's my lowest level piece, which will ALWAYS be consumed as a partial view inside other form tags:
namespace MVC3App.ViewModels
{
public class Payment : IValidatableObject
{
public decimal Amount { get; set; }
public int CreditCardNumber { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Amount < 20)
yield return new ValidationResult("Please pay more than $20", new string[] { "Amount" });
}
}
}
And here's the 'main' ViewModel that includes it:
namespace MVC3App.ViewModels
{
public class NewCustomerWithPayment :IValidatableObject
{
public string Name { get; set; }
public int Age { get; set; }
public ViewModels.Payment PaymentInfo { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Age < 18)
yield return new ValidationResult("Too young.", new string[] { "Age" });
}
}
}
For the View of the NewCustomerWithPayment, I have this:
#model MVC3App.ViewModels.NewCustomerWithPayment
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>NewCustomerWithPayment</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.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
</fieldset>
#Html.Partial("Payment")
<p><input type="submit" value="Create" /></p>
}
And the Partial View "Payment" is ALWAYS rendered inside another Html.Beginform tag, it just has this:
#model MVC3App.ViewModels.Payment
<h2>Payment</h2>
<fieldset>
<legend>Payment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Amount)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Amount)
#Html.ValidationMessageFor(model => model.Amount)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CreditCardNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CreditCardNumber)
#Html.ValidationMessageFor(model => model.CreditCardNumber)
</div>
</fieldset>
My problem is that I cannot get the Validation on the 'Payment' viewmodel to work. Can anyone with experience using IValidatableObject on ViewModels which are rendered as Partial Views chime in and give me a validation pattern that works? I can live without JavaScript validation if I have to.
These answers all have some great info, but my immediate issue was resolved by using this:
#Html.EditorFor(model => model.PaymentInfo)
Instead of this:
Html.Partial("Payment", Model.PaymentInfo)
I was surprised that this worked, but it does. The EditorFor helper renders out the partial view just like Html.Partial, and wires in the validation automatically. For some reason, it does call the validation twice on the child model (Payment in my example), which seems to be a reported issue for some other people (http://mvcextensions.codeplex.com/workitem/10), so I have to include a boolean for 'HasBeenValidated' on each model and check for it at the beginning of the Validate call.
Update: you must move your view to the EditorTemplates folder under /Views/Shared/ in order for the view to be used by the EditorFor helper. Otherwise, the EditorFor will give you the default editing fields for the types.
Here is a lame example of a custom validator for a checkbox :) I would write a custom validator or use a regex maybe. This may get you on the right path and be easier.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CheckBoxMustBeTrueAttribute : ValidationAttribute, IClientValidatable
{
#region IClientValidatable Members
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredcheckbox"
};
}
#endregion
public override bool IsValid(object value)
{
if (value is bool)
{
return (bool) value;
}
return true;
}
}
Most probably IValidatableObject is recognized only on the root model. You can call the inner model Validate method from the root model:
public class NewCustomerWithPayment :IValidatableObject {
...
public ViewModels.Payment PaymentInfo { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Age < 18)
yield return new ValidationResult("Too young.", new string[] { "Age" });
if (this.PaymentInfo != null)
yield return this.PaymentInfo.Validate(validationContext);
}
}
Note: Not sure if the above compiles.
I am using nhibernate and mvc3 in asp.net
I'm trying to add data into table where my table schema is like this:
public class HobbyMasters
{
[Key]
public virtual int HobbyId { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "NameRequired")]
public virtual string HobbyName { get; set; }
public virtual HobbyTypes HobbyType { get; set; }
[Required]
public virtual string HobbyDetails { get; set; }
[Required]
public virtual ICollection<HobbyMasters> HobbyNames { get; set; }
}
public class HobbyTypes
{
[Key]
public virtual int HobbyTypeId { get; set; }
[Required]
public virtual string HobbyType { get; set; }
public virtual ICollection<HobbyTypes> Hobby { get; set; }
}
in my Controller
public ActionResult Create()
{
ViewBag.c1 = (ICollection<HobbyTypes>)(new Hobby_MasterService().GetAllHobbyTypes());
return View();
}
//
// POST: /Hobbies/Create
[HttpPost]
public ActionResult Create(HobbyMasters hobby)
{
ViewBag.c1 = (ICollection<HobbyTypes>)new Hobby_MasterService().GetAllHobbyTypes();
try
{
if (ModelState.IsValid)
{
new Hobby_MasterService().SaveOrUpdateHobby(hobby);
return RedirectToAction("Index");
}
}
}
in the view:
#using (Html.BeginForm("Create", "Hobbies", FormMethod.Post))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Hobby Master</legend>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HobbyName)
#Html.ValidationMessageFor(model => model.HobbyName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyType)
</div>
<div class="Editor-field">
#Html.DropDownListFor(model =>model.HobbyType.HobbyTypeId, new SelectList(ViewBag.c1, "HobbyTypeId", "HobbyType"), "-- Select --")
#Html.ValidationMessageFor(model => model.HobbyType)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyDetails)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HobbyDetails)
#Html.ValidationMessageFor(model => model.HobbyDetails)
</div>
</fieldset>
<p><input type="Submit" value="Create" /> </p>
}
Apparently i found that My Modelstate.IsValid is always false.....
since it stores only the HobbyId and getting Hobby Type as null the HobbyMasters hobbytype object.....
dnt knw where i'm going wrong may be in dropdownlist or something else.....
Plaese help me asap:(
There are a couple of issues with your code:
First is the fact that you decorated HobbyNames collection property with the [Required] attribute. You should use this attribute only on simple properties. In fact you could leave the property but it will have absolutely no effect
The second issue with your code is that you have decorated the HobbyType string property of the HobbyTypes model with a [Required] attribute but you never use this property in your view. So no value is sent when you submit the form and your model is invalid.
Another issue with your code is that you bound the dropdown list to the model => model.HobbyType.HobbyTypeId property. But the HobbyTypeId is not a nullable type. And yet you made your dropdown contain a default value: "-- Select --". This is not possible. If you want to have a dropdown list with an optional value you must bind it to a nullable property on your model.
I have tried to clean up your code a little.
Model:
public class HobbyMasters
{
public virtual int HobbyId { get; set; }
[Required]
public virtual string HobbyName { get; set; }
public virtual HobbyTypes HobbyType { get; set; }
[Required]
public virtual string HobbyDetails { get; set; }
public virtual ICollection<HobbyMasters> HobbyNames { get; set; }
}
public class HobbyTypes
{
[Required]
public virtual int? HobbyTypeId { get; set; }
public virtual string HobbyType { get; set; }
public virtual ICollection<HobbyTypes> Hobby { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Create()
{
ViewBag.c1 = (ICollection<HobbyTypes>)(new Hobby_MasterService().GetAllHobbyTypes());
var model = new HobbyMasters();
return View(model);
}
//
// POST: /Hobbies/Create
[HttpPost]
public ActionResult Create(HobbyMasters hobby)
{
if (ModelState.IsValid)
{
try
{
new Hobby_MasterService().SaveOrUpdateHobby(hobby);
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
}
}
ViewBag.c1 = (ICollection<HobbyTypes>)new Hobby_MasterService().GetAllHobbyTypes();
return View(hobby);
}
}
View:
#model HobbyMasters
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Hobby Master</legend>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HobbyName)
#Html.ValidationMessageFor(model => model.HobbyName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyType)
</div>
<div class="Editor-field">
#Html.DropDownListFor(model => model.HobbyType.HobbyTypeId, new SelectList(ViewBag.c1, "HobbyTypeId", "HobbyType"), "-- Select --")
#Html.ValidationMessageFor(model => model.HobbyType.HobbyTypeId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyDetails)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HobbyDetails)
#Html.ValidationMessageFor(model => model.HobbyDetails)
</div>
</fieldset>
<p><input type="Submit" value="Create" /></p>
}
Also I would very strongly recommend you to use view models. Don't pass your domain entities to your views. Define view models.
I would like to add a textbox (date) and button to my report, which filters the data.
The below mvc is working, but the input must still be validated (must be a DATE) on client side (and server side if possible)
My Model looks like this :
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
View :
#model IEnumerable<project_name.Models.DailyReport>
#* text box and button: *#
#using (Html.BeginForm("Index", "DailyReport", FormMethod.Get))
{ <p>
Title: #Html.TextBox("SearchDateString")
<input type="submit" value="Filter" />
</p>
}
#* display dates*#
#foreach (var item in Model)
{ #Html.DisplayFor(modelItem => item.ReportDate)
}
my controller:
public ViewResult Index(String SearchDateString)
{
var dailyreport = db.DailyReport.Include(d => d.Site);
if (!String.IsNullOrEmpty(SearchDateString))
{
DateTime search_date = Convert.ToDateTime(SearchDateString);
dailyreport = dailyreport.Where(r => r.ReportDate == search_date);
}
return View(dailyreport.ToList());
}
Can someone help me please?
How do I make sure a valid date is entered in the textbox?
Should I create a another model with a date field for this input?
Utilize the DataTypeAttribute from the DataAnnotations namespace in your Model, like so:
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
public class DrViewModel
{
[DataType(DataType.Date)]
public string DateTimeSearch { get; set; }
List<DailyReport> DailyReports { get; set; }
}
In your View, have something like:
#model project_name.Models.DrViewModel
#using (Html.BeginForm("Index", "DailyReport", FormMethod.Get))
{
<p>
Title: #Html.TextBoxFor(m => m.DateTimeSearch)
<input type="submit" value="Filter" />
</p>
}
#foreach (var item in Model.DailyReports)
{
#Html.DisplayFor(m => item.ReportDate)
}
#Shark Shark pointed me in the right direction to use a viewmodel, this is the end result that is now working. JS validation added as well.
(DBSet was not necessary because DrViewModel is a viewmodel.)
controllers :
public ActionResult Index(DrViewModel dvm)
{
var dailyreport = db.DailyReport.Include(d => d.Site);
if (dvm.DateTimeSearch != null)
{
dailyreport = dailyreport.Where(r => r.ReportDate == dvm.DateTimeSearch);
}
dvm.DailyReport = dailyreport.ToList();
return View(dvm);
}
models :
public class DrViewModel
{
public DateTime? DateTimeSearch { get; set; }
public List<DailyReport> DailyReport { get; set; }
}
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
view :
#model myproject.Models.DrViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</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("Index","DailyReport", FormMethod.Get ))
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.DateTimeSearch)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateTimeSearch)
#Html.ValidationMessageFor(model => model.DateTimeSearch)
<input type="submit" value="Filter" />
</div>
}
#foreach (var item in Model.DailyReport)
{
#Html.DisplayFor(modelItem => item.ReportDate)
}