I've got a form where a description field will be required in some situations, and not required in others. Originally, it was always required and I had the required attribute in the model, like so:
[DisplayName("Description")]
[Required(ErrorMessage = "Please enter a description.", AllowEmptyStrings = false)]
public string PaymentDescription { get; set; }
Now we only want this to be required only under some circumstances. So I removed the required data annotation from the model. And I tried a few things, including this:
@if (isDescriptionRequired)
{
@Html.TextBoxFor(m => m.PaymentDescription, new { @class = "form-control", @placeholder = "Description", @maxlength = "200" })
@Html.ValidationMessageFor(m => m.PaymentDescription, "Please enter a description.")
}
else
{
@Html.TextBoxFor(m => m.PaymentDescription, new { @class = "form-control", @placeholder = "Description", @maxlength = "200" })
}
But that's not working. What is the most appropriate way to dynamically set the required attribute for a model property based on some data condition? I'm open to other approaches outside sticking the logic in the view. The bottom line is I need that field to be required sometimes, and not required other times. Thanks for your help.
=== Edit 8/21/2015 at 8:00 am CST ===
After investigating MVC Foolproof Validation, and some other [RequiredIf] extensions, I opted for something simpler. It's unclear to me if foolproof works with MVC 4, and other solutions seemed unnecessarily complex, lacked client-side validation, or lacked sufficient cross-browser testing for the client-validation scripts.
Stick 2 properties in the Model to represent a single property:
// Property 1 of 2 to represent Description. Work around for requiring Description in special cases.
[DisplayName("Description")]
public string PaymentDescription { get; set; }
// Property 2 of 2 to represent Description. Work around for requiring Description in special cases.
[DisplayName("Description")]
[Required(ErrorMessage = "Payment description is required.", AllowEmptyStrings = false)]
public string PaymentDescriptionRequired { get; set; }
In the View conditionally display the controls for the relevant property.
if (Model.RequirePaymentApproval)
{
@Html.LabelFor(m => m.PaymentDescriptionRequired, new { @class = "control-label" })
@Html.TextBoxFor(m => m.PaymentDescriptionRequired, new { @class = "form-control", @placeholder = "Description", @maxlength = "200" })
@Html.ValidationMessageFor(m => m.PaymentDescriptionRequired)
}
else
{
@Html.LabelFor(m => m.PaymentDescription, new { @class = "control-label" })
@Html.TextBoxFor(m => m.PaymentDescription, new { @class = "form-control", @placeholder = "Description", @maxlength = "200" })
@Html.ValidationMessageFor(m => m.PaymentDescription)
}
Then in the Controller post evaluate the conditional property bool (RequirePaymentApproval), and assign the required value to the non-required property, or remove the required property from ModelState.
if (model.RequirePaymentApproval)
{
model.PaymentDescription = model.PaymentDescriptionRequired;
}
else
{
ModelState.Remove("PaymentDescriptionRequired");
}
if (ModelState.IsValid) {
// Save changes
}
This approach has the benefit of being able to use the built in Microsoft data annotation validation code, which has a proven record of being cross-browser compliant. And it's relatively easy to follow. What I don't like is that it seems verbose, and I dislike sticking the conditional validation logic in both the view and the controller.
Perhaps Microsoft will extend the data annotation library in the future to handle more real-world scenarios like this.
I'm leaving this open for now because better developers than I will likely have more elegant recommendations. Thanks.
Related Research:
- http://forums.asp.net/t/1924941.aspx?Conditional+Validation+using+DataAnnotation
- http://foolproof.codeplex.com/
- http://andrewtwest.com/2011/01/10/conditional-validation-with-data-annotations-in-asp-net-mvc/
- http://www.compiledthoughts.com/2011/02/aspnet-mvc-excluding-model-validation.html
- http://blogs.msdn.com/b/stuartleeks/archive/2011/10/06/flexible-conditional-validation-with-asp-net-mvc-3.aspx
- Is it correct way to use ModelState.Remove to deal with ModelState?