MVC data annotation on client side conditional - c#

i have a form wherein there is conditional textboxes.i have used mvc dataannotation client side on dropdown change i hide two textboxes data validation error is not fired but in controller i get model error in if (ModelState.IsValid).How can i do condtional handling of data annotation in client side only.I dont want to use fullproof validation or other third party.
i tried removing the data-val-* attributes using jquery still getting error in controller.refer image if i select asset type laptop then sim plan and price is hidden dataannotation dont fire which is correct but get error on controller.
Model:
[Required(ErrorMessage = "Please Enter Make")]
public string Make { get; set; }
[Required(ErrorMessage = "Please Enter Model")]
public string Model { get; set; }
[Required(ErrorMessage = "Please Enter Sim Plan")]
public string SimPlan { get; set; }
[Required(ErrorMessage = "Please Enter Price")]
public decimal? Price { get; set; }

there's a much better way to add conditional validation rules in MVC3. Have your model inherit IValidatableObject and implement the Validate method:
public class Person : IValidatableObject
{
public string Name { get; set; }
public bool IsSenior { get; set; }
public Senior Senior { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (IsSenior && string.IsNullOrEmpty(Senior.Description))
yield return new ValidationResult("Description must be supplied.");
}
}
see more of a description at http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

If you trying to manually clear validation error which u have not used in your view than i would suggest you clear them before checking modelstate.
Ex:
[HttpPost]
public ActionResult Register(Model objModel )
{
foreach (string Key in ModelState.Keys)
{
if ((Key.Equals("Email")) || (Key.Equals("Password")))
{
ModelState[Key].Errors.Clear();
}
}
if (ModelState.IsValid)
{
//Do the work
}
}
In the above example i have passed the values in Model and i didn't pass any value for Email and password, so in controller i am clearing those Key which is present in ModelState.

Related

Join multiple Model properties into 1 property and get one error message for each of them

I have 4 string properties, each property for each textbox in my Asp.Net MVC view.
I also have a property that concats the values of each into one:
public string ModelCode {get{return ProjNr+SerialNr+UserNr+ClientNr}}
So I want, if one of the properties doesn't fit the model requirements to get just one error message. Like when the user doesn't input the ClientNr instead of getting an error saying "Client required" it should show an error saying that the ModelCode doesn't meet the requirements.
EDIT:
ProjNr, SerialNr, UserNr and ClientNr are required and they are strings.
You can implement IValidatableObject by your model class
public class YourModel : IValidatableObject
{
public string ProjNr { get; set; }
public string SerialNr { get; set; }
public string UserNr { get; set; }
public string ClientNr { get; set; }
public string ModelCode => $"{ProjNr}{SerialNr}{UserNr}{ClientNr}";
// ...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (/*check if ProjNr or other fields not valid*/)
yield return new ValidationResult(
"ModelCode doesn't meet the requirements",
new [] {"ModelCode"}); // return only ModelCode member
}
}
Another option (if you want to rely on DataAnnotation attributes for validation of ProjNr, SerailNr, UserNr and ClientNr instead of validating them manually - you can check validation errors of those properties in controller and add new validation error if any errors found:
var hasModelCodeErrors = ModelState["ProjNr"].Errors.Any()
|| ModelState["SerialNr"].Errors.Any()
|| ModelState["SerialNr"].Errors.Any()
|| ModelState["ClientNr"].Errors.Any();
if (hasModelCodeErrors)
ModelState.AddModelError("ModelCode", "ModelCode doesn't meet the requirements");

Multiple view models in Razor view

What would be the best way to for a razor view to handle multiple models? for an MVC3 application.
I have two models, both similar, but the Postcode field is required for one model and not for another
public class IrelandPostcodeLookupViewModel , IWithProgress
{
readonly Progress _Progress = new Progress(Step.Delivery);
public Progress Progress
{
get { return _Progress; }
}
[Required(ErrorMessage = "Please enter your house number or name")]
[DisplayName("House number or name")]
public string HouseNumber { get; set; }
[StringLengthWithGenericMessage(50)]
[DisplayName("Eircode")]
public string Postcode { get; set; }
}
public class PostcodeLookupViewModel , IWithProgress
{
readonly Progress _Progress = new Progress(Step.Delivery);
public Progress Progress
{
get { return _Progress; }
}
[Required(ErrorMessage = "Please enter your house number or name")]
[DisplayName("House number or name")]
public string HouseNumber { get; set; }
[StringLengthWithGenericMessage(50)]
[Required(ErrorMessage = "Please enter your postcode")]
[DisplayName("PostCode")]
public string Postcode { get; set; }
}
In the controller I want to use a particular view model depending on a country I am passed. Something like
public virtual ActionResult PostcodeLookup(string country)
{
if (country == Country.UnitedKingdom)
return View(new PostcodeLookupViewModel());
else
return View(new IrelandPostcodeLookupViewModel());
}
I was handling this in the view with
#model dynamic
The problem I have with this is my view contains partial views
#Html.Partial("~/Views/Shared/_Progress.cshtml", Model.Progress)
and I run into the error 'HtmlHelper' has no applicable method named 'Partial' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched'
Can anyone advise how I can handle the Partial View?
Thanks
Because Model is dynamic, also Model.Progress produces a dynamic.
This is true for all properties and function calls on a dynamic object, no matter how deep you'd go.
To solve this you can typecast the Model.Progress object:
#Html.Partial("~/Views/Shared/_Progress.cshtml", (Progress)Model.Progress)

Fluent Validation Inconsistent with ASP.NET MVC 5

I'm using Fluent Validation v5.5 with ASP.NET v5.2.2 and I'm getting some inconsistent results with the validation.
My view model is:
public class QuoteViewModel
{
[Display(Name = #"Day")]
public int DateOfBirthDay { get; set; }
[Display(Name = #"Month")]
public int DateOfBirthMonth { get; set; }
[Display(Name = #"Year")]
public int DateOfBirthYear { get; set; }
[Display(Name = #"Gender")]
public Gender? Gender { get; set; }
[Display(Name = #"State")]
public int StateId { get; set; }
}
My controller method is:
public ActionResult Quote(QuoteViewModel viewModel)
{
var _validator = new QuoteValidator();
var results = _validator.Validate(viewModel);
if (!ModelState.IsValid)
{
return Json(false);
}
return Json(true);
}
My validator is:
public class QuoteValidator : AbstractValidator<QuoteViewModel>
{
public QuoteValidator()
{
RuleFor(x => x.Gender).NotEmpty();
RuleFor(x => x.StateId).NotEmpty();
RuleFor(x => x.DateOfBirthDay).NotEmpty().InclusiveBetween(1, 31);
RuleFor(x => x.DateOfBirthMonth).NotEmpty().InclusiveBetween(1, 12);
RuleFor(x => x.DateOfBirthYear).NotEmpty().LessThanOrEqualTo(DateTime.UtcNow.Year);
}
}
I'm running a test that posts all blank value form fields. Thus the view model fields retain default values after the view model object is created.
For comparison, in the controller I'm running the validation explicitly and the results aren't consistent with the validation result in ModelState.
ModelState is showing 4 errors, all triggered by NotEmpty rules. NotEmpty on the nullable enum Gender doesn't seem to trigger.
The explicit validation is returning 7 out of 8 errors, the LessThanOrEqualTo rule won't fire since the DateOfBirthYear defaults to zero.
My pain point is I can't figure out why ModelState is missing the NotEmpty error on the nullable enum Gender.
The only way I've been able to trigger that error is to post just the Gender value.
Please help.
EDIT:
After stepping through some code, it appears that the issue is related to the Fluent Validation RequiredFluentValidationPropertyValidator. The Gender field is a nullable value type which is bound to null. The following snippet from RequiredFluentValidationPropertyValidator prevents validation:
ShouldValidate = isNonNullableValueType && nullWasSpecified;
!ModelState.IsValid doesn't use your validation result it uses defaulf MVC validation (that can be added through DataAnnotations). You have to check !results.IsValid instead which contains the validation result of your QuoteValidator.
If you want to use default ModelState.IsValid you have to mark your model with validator attribute:
[Validator(typeof(QuoteValidator))]
public class QuoteViewModel
{
[Display(Name = #"Day")]
public int DateOfBirthDay { get; set; }
[Display(Name = #"Month")]
public int DateOfBirthMonth { get; set; }
[Display(Name = #"Year")]
public int DateOfBirthYear { get; set; }
[Display(Name = #"Gender")]
public Gender? Gender { get; set; }
[Display(Name = #"State")]
public int StateId { get; set; }
}
And add the following line to your Application_Start method:
protected void Application_Start() {
FluentValidationModelValidatorProvider.Configure();
}

validating the incoming request that depends on other entity's property value?

I know there's data annotation to validate data such as [Required], [Range()] and so on. Not to mention the unobtrusive validation that makes it easier in the client side. Many thanks to them.
But what about if you need to validate the value that depends on the entity's property value? I have this scenarion, say:
My models:
public class Schedule
{
public int Id { get; set; }
public DatetimeOffset StartDate { get; set; }
public DatetimeOffset EndDate { get; set; }
}
Now in a form,
<input type="text" name="StartDate" />
<input type="text" name="EndDate" />
How would you validate that the EndDate should not be less than the StartDate? Is there a built-in attribute in data annotation for that? Or should make a custom one? It would be great if it would make use of the unobstrusive validation by microsoft.
Here's another scenario:
What if you would do validation that depends on the value that is saved in the db? Say,
public class Bag
{
//constructor
public int Capacity { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item
{
//constructor
public string Description { get; set; }
public virtual ICollection<Bag> Bags { get; set; }
}
That if you would do validation on the Items being added to the Bag but if the user tries to input beyond the limit of the Bag's Capacity, should show the validation error.
Is this possible?
I'm currently using ASP.NET MVC 4. EF5 Code first approach.
The first approach is to implement IValidatableObject:
public class Schedule : IValidatableObject
{
public int Id { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (StartDate > EndDate)
{
yield return new ValidationResult("End date must be greater than start date.", new [] { "EndDate"});
}
}
}
It will be executed automatically during model binding on server side.
If you want to validate on client side, you also have options. One of them is remote validation. You can read about it here. To summarize: You have to create constroller and action, that takes validated values and returns if it is valid or not. It can take more than one value. For example ID and username if you want to check uniqueness.
Obviously sending values to server is not necessary in date comparison. You can implement your own validation attribute to handle comparison on client side. Someone tried to implement it here.

DataAnnotation to compare two properties

Is there any way of using data annotations to compare two form field (eg. to confirm an email address) are the same, before allowing the form to be posted?
eg. can the regular expression data annotation use the match function to reference another property in a ViewModel?
Use the CompareAttribute
public string EmailAddress {get; set;}
[Compare(nameof(EmailAddress), ErrorMessage = "Emails mismatch")]
public string VerifiedEmailAddress { get; set; }
As one possibe option self-validation:
Implement an interface IValidatableObject with method Validate, where you can put your validation code.
public class TestModel : IValidatableObject
{
public string Email{ get; set; }
public string ConfirmEmail { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Email != ConfirmEmail)
{
yield return new ValidationResult("Emails mismatch", new [] { "ConfirmEmail" });
}
}
}
Please notice: this is only server-side validation.

Categories

Resources