1

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:

Community
  • 1
  • 1
Ken Palmer
  • 2,355
  • 5
  • 37
  • 57
  • **How** is it not working? What happens? – SLaks Aug 20 '15 at 20:22
  • Validation is not even being triggered. The form just lets me submit without entering a description. Even if I just manually set the isDescriptionRequired bool in the above example to true. – Ken Palmer Aug 20 '15 at 20:27
  • That has nothing to do with the condition. Do you have client-side validation set up? – SLaks Aug 20 '15 at 20:38
  • 1
    You can use a [foolproof](http://foolproof.codeplex.com/) `[RequiredIfTrue]` of similar validation attribute to your property –  Aug 20 '15 at 21:44
  • Thanks. Yes I have client-side validation set up. The jquery.validate.js and jquery.validate.unobtrusive.js scripts are referenced. Thanks for the tip on foolproof, I'll investigate that. – Ken Palmer Aug 21 '15 at 11:27
  • Install the nuget package, add `[RequiredIfTrue("RequirePaymentApproval")]` to the `PaymentDescription` property then include `@Html.HiddenFor(m => m.RequirePaymentApproval)` (or a `CheckBoxFor() if you want to edit it in the view) then you don't need the other property. And it does work with MVC-4 and gives you both server and client side validation. –  Aug 22 '15 at 09:05

0 Answers0