I have a model in mvc as below
public class person
{
[Required(ErrorMessage = "Please enter First Name.")]
public string first_name {get;set;}
[Required(ErrorMessage = "Please enter last Name")]
public string last_name {get;set;}
[Required(ErrorMessage = "Please enter |DOB")]
public DateTime DOB {get;set;}
}
post method
[HttpPost]
public ActionResult save_person(person per)
{
if(per.first_name == null || per.first_name =="")
per.first_name ="demo_first";
if(per.lastname == null || per.lastname =="")
per.last_name ="demo_last";
if (ModelState.IsValid) //fails even assignment is done above
{
}
}
so using if condition I make sure the model elements will contain atleast some value but even after that
ModelState.IsValid is failing and returning back to the view saying first_name and last_name is requried
how can we achieve this logic??
It is quite unclear why are the first_name and last_name properties on your model decorated with the Required attribute if they clearly are not required.
This being said, if you want to update the value of some model property in your controller you might need to ensure that you also update it in the ModelState:
if (per.first_name == null || per.first_name == "")
{
per.first_name ="demo_first";
ModelState.Remove("first_name");
ModelState.SetModelValue("first_name", new ValueProviderResult(per.first_name, per.first_name, CultureInfo.InvariantCulture));
}
if (per.last_name == null || per.lastname == "")
{
per.last_name ="demo_last";
ModelState.Remove("last_name");
ModelState.SetModelValue("last_name", new ValueProviderResult(per.last_name, per.last_name, CultureInfo.InvariantCulture));
}
if (ModelState.IsValid)
{
...
}
I just want to add some reference to the actual documentation to clear things up for the OP on why changing properties in the method does not solve his problem.
From the docs:
Handling Model State Errors
Model validation occurs prior to each
controller action being invoked, and it is the action method’s
responsibility to inspect ModelState.IsValid and react appropriately.
In many cases, the appropriate reaction is to return some kind of
error response, ideally detailing the reason why model validation
failed.
That means, that validation has already happened when your ActionResult is invoked and the .IsValid property is set.
As per your comment: If the same model needs to be used in a different view, but with different validation requirements, it would be a good idea to create a new ViewModel with appropriate annotations:
public class AnotherPersonViewModel {
//no required attribute
public string last_name {get; set;}
//no required attribute
public string first_name {get; set;}
//... rest of attributes
}
After this you could always map your view model to your entity model or whatever you are currently doing. But you avoid cluttering your controller with unnecessary code to remedy mistakes in your architecture.
Related
I have the following view model:
public class CreateCaseViewModel
{
[Required]
public string Subject { get; set; }
[Required]
[DisplayName("Post Content")]
[UIHint("ForumEditor"), AllowHtml]
[DataType(DataType.MultilineText)]
public string PostContent { get; set; }
// some other dropdown properties
}
The following controller action:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create(CreateCaseViewModel viewModel, FormCollection collection)
{
// Re-populate dropdowns
viewModel.Categories = _unitOfWork.CategoryRepository.GetCategories();
viewModel.Subject = collection["Subject"];
viewModel.PostContent = collection["Description"];
try
{
if (ModelState.IsValid)
{
// Do stuff
}
}
catch (DataException dex )
{
throw new ApplicationException("Something :", dex);
}
return View(viewModel);
}
I am manually assigning the value to PostContent from a value in FormCollection as you can see from code above. However I still keep getting modelstate is invalid - I'm returned back to the view with the validation error saying `The Post Content field is required'
Why is modelstate invalid?
When you submit the form the model binder will read the posted request data and map it to your method parameter. After that model validation framework will do the validation. It does not look at your FormCollection for doing this. So in your case, your model validation is failing because as per your view model it is expecting a value for PostContent property and it is not available there. Your action method code where you are setting the value of it gets executed later ( by this time model validation already occurred).
Your options are, either standardize the input element name with your view model property name (rename the PostContent to Description or vice versa)
public class CreateCaseViewModel
{
[Required]
public string Subject { get; set; }
[Required]
[DisplayName("Post Content")]
[UIHint("ForumEditor"), AllowHtml]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
Now let the model binder maps the request body to your view model parameter. Remove the manual assignment from the FormCollection in your action method
Or you can probably create a new custom model binder which does the custom mapping for you (same as what you did in your action method).
I would go with option one. Let the default model binder takes care of it.
The model is validated before it is passed to your controller action. Modifying the model does not change that.
You need to call ModelState.Clear() followed by Controller.TryValidateModel(model) to re-validate the model and reset the IsValid property.
Model:
public class Person : IValidatableObject
{
public Address ResidentialAddress { get; set; }
public Address PostalAddress { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}
in Model:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(PostalAddress.Address1))
{
yield return
new ValidationResult("Postal address is required",
new[] { nameof(PostalAddress.Address1) });
}
}
View: (A partial view for address inside the View for Person)
#Html.ValidationMessageFor(m => m.Address1)
In the html this comes out with the name PostalAddress.Address1 and the id PostalAddress_Address1
Unfortunately nameof(PostalAddress.Address1) just returns Address1.
I have tried replacing it with PostalAddress.Address1 and PostalAddress_Address1 and can't get the error to show up.
What's the secret?
I think #MikeDebela is right in the comment below your answer. Your model needs to implement IValidatableObject if you're going to use custom model validation like that. However, that's not your only problem.
First, is there a particular reason you're not just relying on the [Required] attribute for this? Custom model validation is a bit of a waste for something this simple. If the issue is that this is your actual entity class, and you don't want the Address1 column non-nullable at the database-level, well, that's what view models are for. Use them. You can make the property required on just your view model. As a best practice, you should never utilize your entity classes directly to post to.
Also, you're never newing up PostalAddress. When the model binder does its thing on post, if no properties of a related class are posted, it leaves the value of the that related class as null. Then, any related classes that are null, are also not validated. As a result, if the only property is Address1 and you don't post Address1, then PostalAddress is null, and no property on it, specifically, Address1, will participate in validation.
I have 2 text fields, text1 and text2, in my view model. I need to validate if text1 is entered then text2 must be entered and vice versa. How can this be achieved in the custom validation in the view model?
thanks.
You can use implement IValidatableObject (from System.ComponentModel.DataAnnotations namespace) for the server side validation on your View Model:
public class AClass : IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public string SecondName { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if( (!string.IsNullOrEmpty(Name) && string.IsNullOrEmpty(SecondName)) || (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(SecondName)) )
yield return new ValidationResult("Name and Second Name should be either filled, or null",new[] {"Name","SecondName"});
}
}
Now it make sure if both Name and SecondName are set, or null, then model is valid, otherwise, it's not.
Look at mvc foolproof validation it does conditional validation. Find it on nuget or http://foolproof.codeplex.com
Edit
The above is really old
I'd recommend MVC Fluent Validation for anything modern https://docs.fluentvalidation.net/en/latest/aspnet.html
You can use JQuery, something like this:
$("input[x2]").hide();
$("input[x1]").keypress(function() {
var textValue = ("input[x1]").val();
if(textValue)
$("input[x2]").show();
})
If you'd like to use a data annotation validator and validation attributes on your model, you should take a look at this:
" attribute dependent on another field"
I have a view model with 2 properties that are optional - ie - not required. The view uses dropdownlistfor() to get values for these two fields, an includes an optionlabel of "" for the blank value.
When posted back to the create action the ModelState has an error for both of these fields saying "A value is required".
Anyone got any clue if this is a bug or a stupid user (ie, me) error?
Thanks
Udpate:
The View Model looks like this:
[DisplayName("Check Digit Type")]
public VMBarcodeMaskCheckDigitType BarcodeMaskCheckDigitType
{
get;
set;
}
[DisplayName("Mask Type")]
[Required(ErrorMessage="Mask type is required")]
public VMBarcodeMaskType BarcodeMaskType
{
get;
set;
}
[DisplayName("Product")]
public VMProduct Product
{
get;
set;
}
The binding in the controller is :
public ActionResult Create()
{
BarcodeMaskViewModel model = new BarcodeMaskViewModel(new VMBarcodeMask(), Domain.GetBarcodeMaskTypes(), Domain.GetBarcodeCheckDigitTypes(), Domain.GetProducts());
return View(model);
}
//
// POST: /Barcode/Create
[HttpPost]
public ActionResult Create(BarcodeMaskViewModel model)
{
try
{
if (ModelState.IsValid)
{
...
}
}
catch (Exception ex)
{
ModelState.AddModelError("*", ex);
}
return View(new BarcodeMaskViewModel(model.BarcodeMask, Domain.GetBarcodeMaskTypes(), Domain.GetBarcodeCheckDigitTypes(), Domain.GetProducts()));
}
I've had this problem too, and I discovered it was actually nothing to do with the optional fields.
It was because I had an auto-generating primary key column for the entity, called 'Id'. MVC2 automatically checked for a value for this, and obviously there wasn't one as it was being auto-generated.
There's an easy way to fix this is to rename the column to BarcodeId etc, rather than just Id. I gave a better explanation here: http://www.ediblecode.com/post/A-value-is-required-with-ASPNET-MVC-2.aspx
That explanation is all assuming you're using LINQ...
Just use Bind(Exclude="Id") before the first parameter of your Create action.
I think this a confirmed bug. See here: http://forums.asp.net/p/1529205/3699143.aspx
I am using server side validation like
public IEnumerable<RuleViolation> GetRuleViolations()
{
if (String.IsNullOrEmpty(Name))
yield return new RuleViolation("Name is Required", "Name");
if (Price == 0)
yield return new RuleViolation("Price is Required", "Price");
yield break;
}
When I left Price as blank, Then It takes 0 as a value.
So I check it with 0.
In my Database Price cannot be null; and I am using LINQ-to-SQL class.
Now my problem is when I left Price blank it gives me two messages.e.g.
A value is required.
Price is Required.
So How do I put custom validation without showing first error message?
Relpy to comment
I am reffering book code of Professional Asp.net MVC 1.0 here.
HTML pages of Book are Here.
usefull page.
public class RuleViolation
{
public string ErrorMessage { get; private set; }
public string PropertyName { get; private set; }
public RuleViolation(string errorMessage)
{
ErrorMessage = errorMessage;
}
public RuleViolation(string errorMessage, string propertyName)
{
ErrorMessage= errorMessage;
PropertyName = propertyName;
}
}
I think you get the first message "A value is required" automatically from the framework because your Price property is a value type, which can never be null.
So when you post a blank field, the framework will usually try to assign null to this property, which is not possible in this case.
If you change the type to nullable:
public double? Price { get; set; }
That particular message should disappear. Then you could change your validation to:
if (Price == null)
yield return new RuleViolation("Price is required", "Price");
The fact that the database field does not allow nulls should not interfere with your viewmodels.
To make what Thomas Eyde wrote above work (without messing with the code), you can...
Open the corresponding .dbml file
Click the "Price" property
In the Visual Studio Properties window, change the Nullable value from False to True
Save the file!
You can now go into your class and add the if statement and VS should not complain.
That's because the Default Model Binder adds that error. You can write your own model binder for that particular object and work directly with the form collection to get more control over validation.