Reputation: 318
I have a page with which i post two view models to the controller, Enquiry and Appointment. Appointment is nested within enquiry. A user can choose to submit an enquiry with our without creating an appointment .
I use the built in MVC required attributes on the view model properties.
My question is, when the user chooses to create an enquiry without an appointment, how can I elegantly ignore the validators on the nested Appointment view model and have ModelState.IsValid return true?
if(!viewModel.CreateAppointment)
{
//ignore the nested view models validation
}
Upvotes: 7
Views: 6727
Reputation: 29
I did similar to Erik - removed error of ignored property from controller's ModelState
.
public async Task<IActionResult> AddRankAssignment(RankAssignmentVm rankAssignmentVm)
{
ModelState.Remove(nameof(rankAssignmentVm.Order));
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
...
}
Upvotes: 0
Reputation: 215
The other option is instead of nesting Appointment inside Enquiry make a ViewModel for your page containing both Appointment and Enquiry Models separately and then you can use Bind attribute with the Property Include or Exclude to selectively select a Model you want to bind or exclude as shown below.
Public Class EnquiryViewModel
{
public Appointment App {get; set;}
public Enquiry Enq {get; set; }
}
[HttpPost]
//Only bind Enquiry model and it's errors.
public ActionResult CreateEnquiryOnly([Bind(Include = "Enq")]EnquiryViewModel enquiry)
{
if(ModelState.IsValid)
{
// Code for enquiry only.
}
}
Upvotes: 0
Reputation: 215
You can make a custom IgnoreModelError attribute as shown below and use 2 separate buttons in your view one for enquiry only and one for appointment.
// C#
public class IgnoreModelErrorAttribute : ActionFilterAttribute
{
private string keysString;
public IgnoreModelErrorsAttribute(string keys)
: base()
{
this.keysString = keys;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
ModelStateDictionary modelState = filterContext.Controller.ViewData.ModelState;
string[] keyPatterns = keysString.Split(new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < keyPatterns.Length; i++)
{
string keyPattern = keyPatterns[i]
.Trim()
.Replace(@".", @"\.")
.Replace(@"[", @"\[")
.Replace(@"]", @"\]")
.Replace(@"\[\]", @"\[[0-9]+\]")
.Replace(@"*", @"[A-Za-z0-9]+");
IEnumerable<string> matchingKeys = _
modelState.Keys.Where(x => Regex.IsMatch(x, keyPattern));
foreach (string matchingKey in matchingKeys)
modelState[matchingKey].Errors.Clear();
}
}
}
[HttpPost]
[IgnoreModelErrors("Enquiry.Appointment")]
public ActionResult CreateEnquiryOnly(Enquiry enquiry)
{
// Code for enquiry only.
}
[HttpPost]
public ActionResult CreateAppointment(Enquiry enquiry)
{
// Code for appointment.
}
Upvotes: 6
Reputation: 318
Here's what i ended up doing.
This allowed me to clear all the errors on the nested Appointments ViewModel.
if (!viewModel.CreateAppointment)
{
foreach (var modelError in ModelState)
{
string propertyName = modelError.Key;
if (propertyName.Contains("AppointmentsViewModel"))
{
ModelState[propertyName].Errors.Clear();
}
}
}
Upvotes: 2
Reputation: 93434
Well, there is no way to "Elegantly" ignore the errors when using standard data attributes.
You have several options though. The quick and dirty (ie unelegant) way is to just clear the relevant errors from the ModelState in your controller.
if (some condition) {
ModelState["controlName"].Errors.Clear();
}
You could also write your own custom data attributes that use conditional testing. Something like described here:
http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
A third approach would be to eschew attributes and use a validation framework, such as FluentValidation
A final option would be to use JavaScript to determine the correct state of data and then modify the form action url to post to a different Action method. Then you could decorate the action method parameters with Bind attributes to exclude the data items you don't want. However, I would not recommend this one because it requires the client to be involved in the server-side validation process.
Upvotes: 3